<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>八股 on Coder_Studio</title>
        <link>https://iamxurulin.github.io/categories/%E5%85%AB%E8%82%A1/</link>
        <description>Recent content in 八股 on Coder_Studio</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <copyright>iamxurulin</copyright>
        <lastBuildDate>Sun, 05 Apr 2026 17:35:33 +0000</lastBuildDate><atom:link href="https://iamxurulin.github.io/categories/%E5%85%AB%E8%82%A1/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Spring Boot &#43; Vue 前后端联调踩坑记录</title>
        <link>https://iamxurulin.github.io/p/spring-boot--vue-%E5%89%8D%E5%90%8E%E7%AB%AF%E8%81%94%E8%B0%83%E8%B8%A9%E5%9D%91%E8%AE%B0%E5%BD%95/</link>
        <pubDate>Fri, 03 Apr 2026 18:24:30 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot--vue-%E5%89%8D%E5%90%8E%E7%AB%AF%E8%81%94%E8%B0%83%E8%B8%A9%E5%9D%91%E8%AE%B0%E5%BD%95/</guid>
        <description></description>
        </item>
        <item>
        <title>别再搞混了！Redis、Jedis、Lettuce、Spring Data Redis、Redisson、Spring Session Redis 一次讲透</title>
        <link>https://iamxurulin.github.io/p/%E5%88%AB%E5%86%8D%E6%90%9E%E6%B7%B7%E4%BA%86redisjedislettucespring-data-redisredissonspring-session-redis-%E4%B8%80%E6%AC%A1%E8%AE%B2%E9%80%8F/</link>
        <pubDate>Wed, 01 Apr 2026 19:17:35 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%88%AB%E5%86%8D%E6%90%9E%E6%B7%B7%E4%BA%86redisjedislettucespring-data-redisredissonspring-session-redis-%E4%B8%80%E6%AC%A1%E8%AE%B2%E9%80%8F/</guid>
        <description></description>
        </item>
        <item>
        <title>一篇搞懂：Java包名、Maven artifactId、Spring application.name到底有啥区别？</title>
        <link>https://iamxurulin.github.io/p/%E4%B8%80%E7%AF%87%E6%90%9E%E6%87%82java%E5%8C%85%E5%90%8Dmaven-artifactidspring-application.name%E5%88%B0%E5%BA%95%E6%9C%89%E5%95%A5%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Fri, 27 Mar 2026 21:33:09 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%B8%80%E7%AF%87%E6%90%9E%E6%87%82java%E5%8C%85%E5%90%8Dmaven-artifactidspring-application.name%E5%88%B0%E5%BA%95%E6%9C%89%E5%95%A5%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;项目文件夹叫&lt;code&gt;codeedge-backend&lt;/code&gt;，Java包名是&lt;code&gt;codeedgebackend&lt;/code&gt;，Maven的&lt;code&gt;artifactId&lt;/code&gt;是&lt;code&gt;codeedge-backend&lt;/code&gt;，Spring配置里还有个&lt;code&gt;spring.application.name&lt;/code&gt;叫&lt;code&gt;codeedge-backend&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这几个到底有啥区别？必须要对上吗？&lt;/p&gt;
&lt;p&gt;其实，这几个是完全不同维度、不同作用、没有任何强制绑定关系的配置，给谁用、管什么、能不能乱改，差别还是挺大的。&lt;/p&gt;
&lt;p&gt;我就用 &lt;code&gt;codeedge-backend&lt;/code&gt; 项目当例子，先从最核心、最容易踩语法坑的 Java 包名说起。&lt;/p&gt;
&lt;h2 id=&#34;java包名-comcodeedgexucodeedgebackend&#34;&gt;Java包名 &lt;code&gt;com.codeedgexu.codeedgebackend&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;这是Java语言强制规范的代码组织方式，是给Java编译器、JVM、Spring Boot看的，用来唯一标识代码类，和项目功能强绑定。&lt;/p&gt;
&lt;h3 id=&#34;作用&#34;&gt;作用
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;唯一标识代码类，避免重名冲突&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你的&lt;code&gt;controller&lt;/code&gt;、&lt;code&gt;config&lt;/code&gt;、&lt;code&gt;service&lt;/code&gt;这些类，全在&lt;code&gt;com.codeedgexu.codeedgebackend&lt;/code&gt;这个包下面。&lt;/p&gt;
&lt;p&gt;Java编译器靠【包名+类名】唯一确定一个类，比如你的启动类全限定名是&lt;code&gt;com.codeedgexu.codeedgebackend.CodeEdgeBackendApplication&lt;/code&gt;，这个名字全局唯一，不会和别人的项目重名冲突。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;决定Spring Boot的Bean扫描范围&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Spring Boot启动的时候，默认只会扫描启动类所在的包以及它的子包。&lt;/p&gt;
&lt;p&gt;比如你的启动类在&lt;code&gt;com.codeedgexu.codeedgebackend&lt;/code&gt;下面，那它只会扫这个包下面的&lt;code&gt;controller&lt;/code&gt;、&lt;code&gt;service&lt;/code&gt;，把它们注册成Bean。&lt;/p&gt;
&lt;p&gt;如果改了代码的包名，但启动类没跟着搬到新包，Spring 就扫不到新包里的 controller、service 这些类，导致这些类不会被注册成 Bean，项目启动失败。&lt;/p&gt;
&lt;h3 id=&#34;小贴士&#34;&gt;小贴士
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Java包名里，绝对不能用横杠&lt;code&gt;-&lt;/code&gt;&lt;strong&gt;。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因为横杠在Java里是减号运算符，不能出现在包名、类名、变量名里，编译器会直接报错。&lt;/p&gt;
&lt;p&gt;这也是为什么我文件夹名可以叫&lt;code&gt;codeedge-backend&lt;/code&gt;，但包名必须写成&lt;code&gt;codeedgebackend&lt;/code&gt;的根本原因——不是我不想写一样的，是Java语法不允许。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Java包名的所有部分必须全小写。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这是Java的行业通用规范，虽然语法允许大写，但不专业，且很多代码规范检查工具会报错。&lt;/p&gt;
&lt;h2 id=&#34;maven-artifactid-codeedge-backend&#34;&gt;Maven artifactId &lt;code&gt;codeedge-backend&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;这是Maven项目的唯一标识，在同一个&lt;code&gt;groupId&lt;/code&gt;下必须唯一，用来在Maven仓库里找到你的项目、打包发布、管理依赖。&lt;/p&gt;
&lt;h3 id=&#34;作用-1&#34;&gt;作用
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;唯一标识你的Maven项目，和别人的项目区分开；&lt;/li&gt;
&lt;li&gt;项目打包、发布到Maven仓库，全靠这个artifactId；&lt;/li&gt;
&lt;li&gt;以后别人要引用你的项目，就是靠这个artifactId写依赖：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.codeedgexu&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;codeedge-backend&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.1-SNAPSHOT&lt;span class=&#34;nt&#34;&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;小贴士-1&#34;&gt;小贴士
&lt;/h3&gt;&lt;p&gt;上线后如果修改了 &lt;code&gt;artifactId&lt;/code&gt;，项目打包出来的jar包名字会变，别人引用你的项目也要同步改artifactId，不然就找不到依赖。&lt;/p&gt;
&lt;p&gt;推荐用&lt;code&gt;kebab-case&lt;/code&gt;短横线分隔的格式（比如&lt;code&gt;codeedge-backend&lt;/code&gt;），可读性好，Maven全兼容，是行业里90%的项目都会用的写法。&lt;/p&gt;
&lt;h2 id=&#34;applicationyml文件中的codeedge-backend&#34;&gt;&lt;code&gt;application.yml&lt;/code&gt;文件中的&lt;code&gt;codeedge-backend&lt;/code&gt;
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;codeedge-backend&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这是Spring Boot应用的官方服务名，核心作用是给微服务注册中心（Nacos）看的，是你这个服务在微服务集群里的唯一标识。&lt;/p&gt;
&lt;h3 id=&#34;作用-2&#34;&gt;作用
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Nacos服务注册的唯一标识&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你的服务启动时，会去Nacos登记，登记的名字就是这个&lt;code&gt;spring.application.name&lt;/code&gt;；&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;微服务之间调用的标识&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以后有其他服务要调用你的接口，就是靠这个名字在Nacos里找到你的服务地址；&lt;/p&gt;
&lt;h3 id=&#34;小贴士-2&#34;&gt;小贴士
&lt;/h3&gt;&lt;p&gt;同样地，上线后如果修改了这个服务名，Nacos里注册的服务名就变了，其他服务再用旧名字找你，就完全找不到了，微服务调用直接报错。&lt;/p&gt;
&lt;p&gt;推荐和Maven的&lt;code&gt;artifactId&lt;/code&gt;保持一致（比如也叫&lt;code&gt;codeedge-backend&lt;/code&gt;），这样你在看Nacos控制台的时候，一眼就能对应上“这个服务对应Maven里的哪个项目”，不会混乱。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】十万用户并发下，Caffeine&#43;Redis&#43;MySQL多级缓存怎么设计？</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E5%8D%81%E4%B8%87%E7%94%A8%E6%88%B7%E5%B9%B6%E5%8F%91%E4%B8%8Bcaffeine-redis-mysql%E5%A4%9A%E7%BA%A7%E7%BC%93%E5%AD%98%E6%80%8E%E4%B9%88%E8%AE%BE%E8%AE%A1/</link>
        <pubDate>Thu, 26 Mar 2026 14:59:06 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E5%8D%81%E4%B8%87%E7%94%A8%E6%88%B7%E5%B9%B6%E5%8F%91%E4%B8%8Bcaffeine-redis-mysql%E5%A4%9A%E7%BA%A7%E7%BC%93%E5%AD%98%E6%80%8E%E4%B9%88%E8%AE%BE%E8%AE%A1/</guid>
        <description>&lt;p&gt;面试官问了这么一道场景题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“平台用了Caffeine本地缓存、Redis分布式缓存、MySQL数据库，现在有十万个用户同时访问，相当于每个用户对应一个请求，这多级缓存该怎么设计？”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;多级缓存就是“分层扛压”，把十万用户的请求，从快到慢、从近到远，一层层分流，别让所有请求都直接打MySQL。&lt;/p&gt;
&lt;h2 id=&#34;设计方案&#34;&gt;设计方案
&lt;/h2&gt;&lt;h3 id=&#34;定好每一层的职责&#34;&gt;定好每一层的职责
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;缓存层级&lt;/th&gt;
          &lt;th&gt;职责&lt;/th&gt;
          &lt;th&gt;放什么数据&lt;/th&gt;
          &lt;th&gt;容量/速度&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Caffeine本地缓存&lt;/td&gt;
          &lt;td&gt;扛超级热点中的超级热点，大幅减少 Redis 压力&lt;/td&gt;
          &lt;td&gt;访问频率 Top10 的极致热门数据（比如首页轮播爆款、正在进行的秒杀活动）&lt;/td&gt;
          &lt;td&gt;容量小（几百MB）、速度极快（纳秒级）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Redis分布式缓存&lt;/td&gt;
          &lt;td&gt;扛&lt;strong&gt;全局通用&lt;/strong&gt;数据，挡MySQL&lt;/td&gt;
          &lt;td&gt;全平台的业务数据（比如商品详情、用户信息、订单数据）&lt;/td&gt;
          &lt;td&gt;容量大（几十GB）、速度快（毫秒级）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;MySQL数据库&lt;/td&gt;
          &lt;td&gt;数据&lt;strong&gt;最终兜底&lt;/strong&gt;，保证数据不丢&lt;/td&gt;
          &lt;td&gt;所有的原始业务数据&lt;/td&gt;
          &lt;td&gt;容量无限、速度慢（秒级）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;缓存预热&#34;&gt;缓存预热
&lt;/h3&gt;&lt;p&gt;平台刚启动的时候，Caffeine和Redis里都是空的，如果这时候十万用户同时进来，所有请求都会直接打MySQL，直接把数据库打垮。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，考虑采用缓存预热：&lt;/p&gt;
&lt;p&gt;平台启动前，先做两级缓存预热：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把访问频率最高的Top1000 个全量热门 Key（比如全平台热门商品、活动规则），提前从 MySQL 里查出来，全量加载到 Redis；&lt;/li&gt;
&lt;li&gt;把其中访问频率最高的Top10 个超级热点 Key（比如首页轮播商品、正在进行的爆款活动），额外加载到每台服务器的 Caffeine 本地缓存，作为最外层的扛压层。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;缓存雪崩击穿穿透&#34;&gt;缓存雪崩、击穿、穿透
&lt;/h3&gt;&lt;h4 id=&#34;缓存雪崩十万个key同时过期&#34;&gt;缓存雪崩：十万个Key同时过期
&lt;/h4&gt;&lt;p&gt;比如你给所有Key都设置了“1小时过期”，结果十万个Key在同一时间全部过期，所有请求直接打MySQL。&lt;/p&gt;
&lt;p&gt;解决这个问题的话，就是别给所有Key都设一样的过期时间，可以设置“1小时 + 0~30分钟的随机值”，&lt;strong&gt;让Key的过期时间分散开&lt;/strong&gt;，避免同时过期；&lt;/p&gt;
&lt;p&gt;对于首页Top10商品这种超级热点数据，还可以设置成“&lt;strong&gt;永不过期&lt;/strong&gt;”，只在数据更新的时候主动删除缓存。&lt;/p&gt;
&lt;h4 id=&#34;缓存击穿超级热门key突然过期&#34;&gt;缓存击穿：超级热门Key突然过期
&lt;/h4&gt;&lt;p&gt;比如“双11爆款商品详情”这个Key，每秒有十万个请求，结果它突然过期了，十万个请求同时打Redis，发现没有，又同时打MySQL，直接把两者都打垮。&lt;/p&gt;
&lt;p&gt;这个问题也可以对超级热点Key&lt;strong&gt;不设过期时间&lt;/strong&gt;，只在更新时主动删；&lt;/p&gt;
&lt;p&gt;或者加&lt;strong&gt;Redis 分布式互斥锁&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;如果 Key 真的过期了，只让第一个抢到锁的请求去查 MySQL，查出来后更新缓存并释放锁；&lt;/p&gt;
&lt;p&gt;其他没抢到锁的请求，每隔 100ms 重试一次查缓存，直到缓存更新完成，彻底避免十万请求同时打 MySQL。&lt;/p&gt;
&lt;h4 id=&#34;缓存穿透查根本不存在的key&#34;&gt;缓存穿透：查根本不存在的Key
&lt;/h4&gt;&lt;p&gt;比如有人恶意攻击，每秒发十万个请求查“id=999999999”的商品（这个商品根本不存在），缓存里没有，直接打MySQL，把数据库打垮。&lt;/p&gt;
&lt;p&gt;这个问题优先采用&lt;strong&gt;布隆过滤器&lt;/strong&gt;拦截：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;平台启动时，提前把 MySQL 里所有商品的 id 全量加载到布隆过滤器里完成预热；&lt;/li&gt;
&lt;li&gt;用户请求进来，先过布隆过滤器，如果布隆过滤器判定这个商品 id 不存在，直接返回【商品不存在】，完全不会打到缓存和 MySQL；&lt;/li&gt;
&lt;li&gt;只有布隆过滤器判定 id 存在，才会继续走【Caffeine→Redis→MySQL】的查询流程。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;兜底方案&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;缓存空值。如果查 MySQL 发现这个 Key 真的不存在，就把【空值】缓存到 Redis 里（设置 5 分钟短过期时间），下次再查这个 Key，直接从 Redis 里拿空值返回，避免重复打 MySQL。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小贴士&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;空值缓存必须设置短过期时间（3-5 分钟），同时限制单 IP 的请求频率，避免恶意攻击生成大量空值缓存撑爆 Redis 内存。&lt;/p&gt;
&lt;h3 id=&#34;十万并发下的-redis-压力分摊方案&#34;&gt;十万并发下的 Redis 压力分摊方案
&lt;/h3&gt;&lt;p&gt;十万个请求同时进来，如果都打到单实例 Redis 上，大概率会直接把 Redis 打宕机，必须做以下 3 件事分摊压力：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;热点隔离，本地优先扛压：把访问频率 Top10 的极致超级热点 Key（比如首页轮播爆款、正在进行的秒杀活动），全量放在每台服务器的 Caffeine 本地缓存里。十万请求里 90% 都能直接在本地命中，Redis 的压力直接降 90%。&lt;/li&gt;
&lt;li&gt;用 Redis Cluster 集群，把数据分片存储在多个节点上，每个节点只扛一部分请求，避免单节点压力过载；&lt;/li&gt;
&lt;li&gt;Redis 主节点只负责数据更新的&lt;strong&gt;写请求&lt;/strong&gt;，多个从节点负责处理十万级的&lt;strong&gt;读请求&lt;/strong&gt;，把读压力分摊到多个从节点上。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;多实例下的本地缓存一致性&#34;&gt;多实例下的本地缓存一致性
&lt;/h3&gt;&lt;p&gt;服务基本都是多实例集群部署的（比如 10 台服务器），每台服务器都有自己独立的 Caffeine 本地缓存，很容易出现数据不一致的问题：&lt;/p&gt;
&lt;p&gt;比如在 A 实例更新了商品价格，删了 A 实例的 Caffeine 和 Redis 缓存，但 B 实例的 Caffeine 里还存着旧价格，用户访问 B 实例就会看到脏数据。&lt;/p&gt;
&lt;p&gt;考虑用&lt;strong&gt;延迟双删 + Redis 发布订阅&lt;/strong&gt;的组合方案解决这个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;延迟双删：保证 Redis 和 MySQL 的数据一致&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;① 先删除 Redis 缓存，让缓存失效；&lt;/p&gt;
&lt;p&gt;② 再更新 MySQL 数据库，写入新数据；&lt;/p&gt;
&lt;p&gt;③ 等待 1 秒（延迟时间必须略大于你的业务接口平均耗时，根据实际压测结果调整，不能盲目设置），再次删除 Redis 缓存。&lt;/p&gt;
&lt;p&gt;延迟的目的是等其他实例已经从 MySQL 读到旧数据、更新到缓存后，再删一次缓存，避免脏数据回写。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;Redis 发布订阅：保证多实例 Caffeine 的数据一致&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;①A实例更新完数据、完成延迟双删后，通过 Redis 的发布订阅功能，给集群里所有服务实例广播一条消息：商品 id=123 的缓存已失效，请删除本地缓存；&lt;/p&gt;
&lt;p&gt;②所有实例收到消息后，立刻删除自己本地Caffeine里对应的Key；&lt;/p&gt;
&lt;p&gt;③下次用户访问任何实例，都会去 Redis 里查询新数据，再更新到本地 Caffeine，保证全集群所有实例的数据一致。&lt;/p&gt;
&lt;h2 id=&#34;完整的请求数据更新全流程&#34;&gt;完整的请求+数据更新全流程
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/a2da2a3c908113eb2f6748a511d86523-c4dbe57e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】你知道登录用到的Token是什么吗？</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E4%BD%A0%E7%9F%A5%E9%81%93%E7%99%BB%E5%BD%95%E7%94%A8%E5%88%B0%E7%9A%84token%E6%98%AF%E4%BB%80%E4%B9%88%E5%90%97/</link>
        <pubDate>Thu, 26 Mar 2026 12:06:32 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E4%BD%A0%E7%9F%A5%E9%81%93%E7%99%BB%E5%BD%95%E7%94%A8%E5%88%B0%E7%9A%84token%E6%98%AF%E4%BB%80%E4%B9%88%E5%90%97/</guid>
        <description>&lt;p&gt;用户注册登录模块几乎是每一个项目都有的，有一次面试的时候，面试官问：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你既然说到了登录，那你知道Token是什么吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一句话：&lt;/p&gt;
&lt;p&gt;Token = 用户登录成功后，服务器给你颁发的一张加密的临时电子通行证，最常用的就是 JWT 格式的 Token。&lt;/p&gt;
&lt;p&gt;它本质上就是一段加密字符串，作用就是证明三件事：你是谁、你已登录、你有权限访问这个接口。&lt;/p&gt;
&lt;h2 id=&#34;为什么登录必须用-token&#34;&gt;为什么登录必须用 Token？
&lt;/h2&gt;&lt;p&gt;因为 HTTP 请求是无状态的——服务器记不住你上一秒是不是登录过。&lt;br&gt;
每一次请求，对服务器来说都是“陌生人”。&lt;/p&gt;
&lt;p&gt;所以必须给你发一张 &lt;strong&gt;Token&lt;/strong&gt;，让你每次访问都带着，证明你已登录。&lt;/p&gt;
&lt;p&gt;不然你每次点一下页面都要重新输账号密码，根本没法用。&lt;/p&gt;
&lt;h2 id=&#34;token-的工作流程&#34;&gt;Token 的工作流程
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/1c878884cfee28709698e4116798ab5d-a20840ba.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;token-的作用&#34;&gt;Token 的作用
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/02d60c623b004b4da5d275f582dda92d-c8317bf7.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;token-和-session-的区别&#34;&gt;Token 和 Session 的区别
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;存的地方不一样&lt;/strong&gt;&lt;br&gt;
Session 把你的身份信息全存在服务器，用户一多就特别占内存；&lt;br&gt;
Token 是把“电子通行证”直接存在你自己的前端设备里，服务器不用存任何用户状态，只需要验一下通行证的真假就行，几乎没额外负担。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分布式场景的适配能力不一样&lt;/strong&gt;&lt;br&gt;
Session 不适合多服务器的分布式场景——就像你在1号窗口办的会员，2号窗口根本不认，得来回同步数据，麻烦得很；&lt;br&gt;
Token 就没这个烦恼，你手里的通行证全平台通用，不管哪个服务器，只要能验过真伪就直接放行，天生就适配分布式。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;说白了，Token 就是为了解决 HTTP 无状态的痛点，给前端和后端之间搭了一座信任的桥梁，不用每次都输账号密码，不用服务器存储大量用户状态，还能完美适配现在的分布式项目。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】平时会收发短信吧？你知道短信里那种 `CodeEdge` 的短链接，点击之后是怎么跳转到长链接的？它是怎么生成的？</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E5%B9%B3%E6%97%B6%E4%BC%9A%E6%94%B6%E5%8F%91%E7%9F%AD%E4%BF%A1%E5%90%A7%E4%BD%A0%E7%9F%A5%E9%81%93%E7%9F%AD%E4%BF%A1%E9%87%8C%E9%82%A3%E7%A7%8D-codeedge-%E7%9A%84%E7%9F%AD%E9%93%BE%E6%8E%A5%E7%82%B9%E5%87%BB%E4%B9%8B%E5%90%8E%E6%98%AF%E6%80%8E%E4%B9%88%E8%B7%B3%E8%BD%AC%E5%88%B0%E9%95%BF%E9%93%BE%E6%8E%A5%E7%9A%84%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E7%94%9F%E6%88%90%E7%9A%84/</link>
        <pubDate>Tue, 24 Mar 2026 21:34:02 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E5%B9%B3%E6%97%B6%E4%BC%9A%E6%94%B6%E5%8F%91%E7%9F%AD%E4%BF%A1%E5%90%A7%E4%BD%A0%E7%9F%A5%E9%81%93%E7%9F%AD%E4%BF%A1%E9%87%8C%E9%82%A3%E7%A7%8D-codeedge-%E7%9A%84%E7%9F%AD%E9%93%BE%E6%8E%A5%E7%82%B9%E5%87%BB%E4%B9%8B%E5%90%8E%E6%98%AF%E6%80%8E%E4%B9%88%E8%B7%B3%E8%BD%AC%E5%88%B0%E9%95%BF%E9%93%BE%E6%8E%A5%E7%9A%84%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E7%94%9F%E6%88%90%E7%9A%84/</guid>
        <description>&lt;p&gt;面试官问：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“平时会收发短信吧？你知道短信里那种 &lt;code&gt;xxxxx&lt;/code&gt; 的短链接，点击之后是怎么跳转到长链接的？它是怎么生成的？”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;点击短链接后发生了什么&#34;&gt;点击短链接后发生了什么？
&lt;/h2&gt;&lt;p&gt;两个字：&lt;strong&gt;重定向&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;用户点击短链接&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;比如用户点击 &lt;code&gt;t.cn/xu0324&lt;/code&gt;；&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;浏览器请求短链接服务器&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;浏览器向 &lt;code&gt;t.cn&lt;/code&gt; 的服务器发送HTTP请求，要访问 &lt;code&gt;/xu0324&lt;/code&gt; 这个路径；&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;服务器查映射关系&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;服务器去数据库或者Redis里查，发现 &lt;code&gt;xu0324&lt;/code&gt; 对应的原始长链接是 &lt;code&gt;https://www.CodeEdge.com/xxx&lt;/code&gt;；&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;&lt;strong&gt;服务器返回重定向响应&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;服务器不返回网页内容，而是返回一个 HTTP 302状态码，并在响应头 &lt;code&gt;Location&lt;/code&gt; 里填上原始长链接；&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;&lt;strong&gt;浏览器自动跳转&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;浏览器收到302状态码，看到 &lt;code&gt;Location&lt;/code&gt; 里的长链接，自动发起新的请求，访问原始长链接；&lt;/p&gt;
&lt;p&gt;之后，用户就看到了原始长链接的页面。&lt;/p&gt;
&lt;h2 id=&#34;那个短链接是怎么生成的&#34;&gt;那个短链接是怎么生成的？
&lt;/h2&gt;&lt;p&gt;目前主流的生成算法有3种：&lt;/p&gt;
&lt;h3 id=&#34;自增id--base62编码&#34;&gt;自增ID + Base62编码
&lt;/h3&gt;&lt;p&gt;用分布式ID生成器（比如雪花算法），生成一个全局唯一的自增数字ID（比如 1, 2, 3, &amp;hellip;），再把这个10进制的数字ID，转换成62进制的字符串（Base62）。&lt;/p&gt;
&lt;p&gt;这种方案因为是自增ID，每个ID对应唯一的Base62字符串，所以绝对唯一，没有哈希冲突，而且不涉及复杂的哈希计算，生成速度极快。&lt;/p&gt;
&lt;h4 id=&#34;小贴士&#34;&gt;小贴士
&lt;/h4&gt;&lt;p&gt;什么是Base62呢？平时用的是10进制（0-9），Base62其实是0-9（10个）、a-z（26个）、A-Z（26个），加起来总共62个字符，所以叫Base62。&lt;/p&gt;
&lt;h3 id=&#34;哈希算法截取&#34;&gt;哈希算法+截取
&lt;/h3&gt;&lt;p&gt;对原始长链接做MD5或SHA-1哈希，得到一个32位（或更长）的哈希字符串，再截取哈希字符串的前6位或8位，作为短链接后缀。&lt;/p&gt;
&lt;p&gt;这种方案对于两个不同的长链接，哈希后的前6位可能会一样，从而导致映射错误；如果发现了冲突，需要换一种截取方式（比如截取中间6位），或者加盐重新哈希，但这样也增加了复杂度。&lt;/p&gt;
&lt;h3 id=&#34;随机字符串生成&#34;&gt;随机字符串生成
&lt;/h3&gt;&lt;p&gt;直接随机生成一个6位的Base62字符串，再去数据库里查，如果这个字符串已经被占用了，就重新生成，直到生成一个没有被占用的。&lt;/p&gt;
&lt;p&gt;这种方案会随着链接越来越多，冲突概率越来越大，可能要重试很多次才能生成一个可用的；并且高并发下，多个请求可能会同时生成同一个随机字符串，导致数据库唯一键冲突，所以不适合高并发场景。&lt;/p&gt;
&lt;h2 id=&#34;为什么要用短链接&#34;&gt;为什么要用短链接？
&lt;/h2&gt;&lt;h3 id=&#34;1-短信的字数限制&#34;&gt;1. 短信的字数限制
&lt;/h3&gt;&lt;p&gt;因为一条短信最多只能发70个汉字（或160个英文字符），如果直接发一个几百个字符的长链接，一条短信根本发不下，要拆成好几条，成本也会相应增加；如果用短链接，只需要十几个字符，就能完美解决字数限制的问题。&lt;/p&gt;
&lt;h3 id=&#34;2-防拦截防屏蔽&#34;&gt;2. 防拦截、防屏蔽
&lt;/h3&gt;&lt;p&gt;长链接里经常包含很多参数，很容易被运营商、手机安全软件识别为“营销链接”或“钓鱼链接”，直接拦截，但如果用短链接的话，看起来很干净，被拦截的概率就会大大降低。&lt;/p&gt;
&lt;h3 id=&#34;3-数据统计和追踪&#34;&gt;3. 数据统计和追踪
&lt;/h3&gt;&lt;p&gt;短链接服务器可以记录谁在什么时候、通过什么设备、点击了这个链接，企业通过这些数据，可以分析营销效果、用户画像、转化率等等。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】你知道ThreadLocal是什么吗</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E4%BD%A0%E7%9F%A5%E9%81%93threadlocal%E6%98%AF%E4%BB%80%E4%B9%88%E5%90%97/</link>
        <pubDate>Sat, 21 Mar 2026 17:34:29 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E4%BD%A0%E7%9F%A5%E9%81%93threadlocal%E6%98%AF%E4%BB%80%E4%B9%88%E5%90%97/</guid>
        <description>&lt;p&gt;ThreadLocal 是线程专属的&lt;strong&gt;本地变量&lt;/strong&gt;，它的设计是给每个线程创建一份独立的变量副本，线程之间完全不共享数据，天然实现线程隔离，全程无锁竞争，性能远高于加锁保证线程安全的方案。&lt;/p&gt;
&lt;p&gt;每个 &lt;code&gt;Thread&lt;/code&gt; 内部都有一个专属的成员变量 &lt;code&gt;ThreadLocalMap&lt;/code&gt;（不是 &lt;code&gt;ThreadLocal&lt;/code&gt; 的！），&lt;code&gt;Key&lt;/code&gt; 是 &lt;code&gt;ThreadLocal&lt;/code&gt; 实例本身（弱引用），&lt;code&gt;Value&lt;/code&gt; 是存的变量（强引用）。&lt;/p&gt;
&lt;p&gt;官话太多，直接上例子。&lt;/p&gt;
&lt;h2 id=&#34;以用户上下文传递举个例子&#34;&gt;以用户上下文传递举个例子
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 用户上下文工具类
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * 用ThreadLocal存当前登录用户的ID，避免层层传参
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;UserContextHolder&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义ThreadLocal，存用户ID&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_ID_THREAD_LOCAL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 设置用户ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_ID_THREAD_LOCAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 获取用户ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_ID_THREAD_LOCAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 必须手动调用！清理ThreadLocal，防止内存泄漏
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;clear&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;USER_ID_THREAD_LOCAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;remove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;和加锁保证线程安全的区别&#34;&gt;和加锁保证线程安全的区别
&lt;/h2&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;对比项&lt;/th&gt;
          &lt;th&gt;加锁&lt;/th&gt;
          &lt;th&gt;ThreadLocal&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;思想&lt;/td&gt;
          &lt;td&gt;多线程排队用同一个变量，保证同一时间只有一个线程能访问&lt;/td&gt;
          &lt;td&gt;每个线程用自己的专属变量，完全不共享&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;性能&lt;/td&gt;
          &lt;td&gt;有锁竞争，高并发下性能差&lt;/td&gt;
          &lt;td&gt;无锁竞争，性能高&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;适用场景&lt;/td&gt;
          &lt;td&gt;多线程必须共享同一个变量的场景（比如共享计数器、共享资源修改）&lt;/td&gt;
          &lt;td&gt;每个线程需要独立变量副本的场景（比如用户上下文、SimpleDateFormat 线程安全）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;小贴士&#34;&gt;小贴士
&lt;/h3&gt;&lt;p&gt;很多人以为ThreadLocalMap的Key是“线程ID”，其实Key是&lt;strong&gt;ThreadLocal&lt;/strong&gt;实例本身，而不是线程ID。&lt;/p&gt;
&lt;p&gt;一个线程可以有多个ThreadLocal实例，每个实例对应一个Value，存到同一个ThreadLocalMap里，Key不同，互不干扰。&lt;/p&gt;
&lt;h2 id=&#34;threadlocal内存泄漏问题&#34;&gt;ThreadLocal内存泄漏问题
&lt;/h2&gt;&lt;h3 id=&#34;java中的4种引用强度&#34;&gt;Java中的4种引用强度
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;强引用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们平时写的 &lt;code&gt;Object obj = new Object()&lt;/code&gt; 就是强引用，只要强引用还在，GC就不会回收这个对象。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;软引用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用 &lt;code&gt;SoftReference&lt;/code&gt; 包装的引用，只有当内存不够用的时候，GC 才会回收这个对象。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;弱引用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用 &lt;code&gt;WeakReference&lt;/code&gt; 包装的引用，只要GC一运行，不管内存够不够，都会回收这个对象。&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;虚引用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用 &lt;code&gt;PhantomReference&lt;/code&gt; 包装的引用，完全不影响对象的生命周期，只是用来在对象被 GC 回收前收到一个通知，做一些收尾工作。&lt;/p&gt;
&lt;h3 id=&#34;threadlocalmap的entry结构&#34;&gt;ThreadLocalMap的Entry结构
&lt;/h3&gt;&lt;p&gt;ThreadLocalMap的Entry是继承自WeakReference的，结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Entry&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WeakReference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Entry&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ThreadLocal&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;super&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Key是弱引用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Value是强引用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;假设创建了一个 &lt;code&gt;ThreadLocal&lt;/code&gt; 实例，有外部强引用指向它，同时把它作为 Key 存到了线程的 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 里；&lt;/p&gt;
&lt;p&gt;当用完 &lt;code&gt;ThreadLocal&lt;/code&gt; 后，没有手动 &lt;code&gt;remove&lt;/code&gt;，而且外部的强引用也没了（比如 &lt;code&gt;threadLocal = null&lt;/code&gt;），因为 &lt;code&gt;Key&lt;/code&gt; 是弱引用，GC 一运行，&lt;code&gt;Key&lt;/code&gt; 就被回收了，变成了 &lt;code&gt;null&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是， &lt;code&gt;Value&lt;/code&gt; 是强引用，只要线程还活着，&lt;code&gt;Value&lt;/code&gt; 就不会被 GC 回收，这就导致了内存泄漏。&lt;/p&gt;
&lt;p&gt;更严重的还有，如果用了线程池，线程会被长期复用，不仅内存泄漏，还会导致用户信息串号、数据错误等严重的业务问题。&lt;/p&gt;
&lt;h3 id=&#34;怎么避免内存泄漏&#34;&gt;怎么避免内存泄漏
&lt;/h3&gt;&lt;p&gt;使用完ThreadLocal后，手动调用 &lt;code&gt;remove()&lt;/code&gt; 方法。&lt;/p&gt;
&lt;p&gt;举个例子，在Spring Boot的拦截器里：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;UserInterceptor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HandlerInterceptor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;preHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletResponse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 从Token里解析出用户ID，存到ThreadLocal&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parseUserIdFromToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UserContextHolder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setUserId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;afterCompletion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletRequest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HttpServletResponse&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 在这里清理，不管请求成功失败，都要清理。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UserContextHolder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;clear&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;&#34;&gt;
&lt;/h3&gt;</description>
        </item>
        <item>
        <title>【面试真题拆解】被问懵的计算机网络7层_5层_4层模型</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E8%A2%AB%E9%97%AE%E6%87%B5%E7%9A%84%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C7%E5%B1%82_5%E5%B1%82_4%E5%B1%82%E6%A8%A1%E5%9E%8B/</link>
        <pubDate>Sat, 21 Mar 2026 09:04:13 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E8%A2%AB%E9%97%AE%E6%87%B5%E7%9A%84%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C7%E5%B1%82_5%E5%B1%82_4%E5%B1%82%E6%A8%A1%E5%9E%8B/</guid>
        <description>&lt;p&gt;好几次面试都被问了计算机网络模型，一次是让我讲讲7层模型，还有让我讲讲5层模型，4层模型的。&lt;/p&gt;
&lt;p&gt;这群面试官完全不按套路出牌，昨天刚问完 7 层模型，今天就换成了 4 层模型。&lt;/p&gt;
&lt;p&gt;我当场脑子一片空白，支支吾吾说不清楚，直接就凉了。&lt;/p&gt;
&lt;p&gt;首先，为什么会有三个版本？&lt;/p&gt;
&lt;p&gt;且看：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;7层模型（OSI参考模型）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这是国际标准化组织定的&lt;strong&gt;理论学术标准&lt;/strong&gt;，是网络分层的“祖宗”，但实际互联网几乎不用。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;4层模型（TCP/IP模型）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;互联网实际在用的&lt;strong&gt;工业落地标准&lt;/strong&gt;，我们现在上网、写代码用的HTTP、TCP、IP，全是基于这个模型跑的，是真正“干活”的模型。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;5层模型（教材简化模型）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;国内计算机教材最常用的&lt;strong&gt;简化教学版&lt;/strong&gt;，把4层模型的最底层拆成了2层，方便理解学习。&lt;/p&gt;
&lt;p&gt;面试的时候，不管面试官问哪个模型，可以先把下面这张对应关系表讲出来，这样就能把三个模型的边界划清楚，也不容易搞混。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;7 层 OSI 参考模型&lt;/th&gt;
          &lt;th&gt;5 层教材简化模型&lt;/th&gt;
          &lt;th&gt;4 层 TCP/IP 工业模型&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;应用层&lt;/td&gt;
          &lt;td&gt;应用层&lt;/td&gt;
          &lt;td&gt;应用层&lt;/td&gt;
          &lt;td&gt;给用户/应用提供服务，比如浏览器、APP&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;表示层&lt;/td&gt;
          &lt;td&gt;（合并到应用层）&lt;/td&gt;
          &lt;td&gt;（合并到应用层）&lt;/td&gt;
          &lt;td&gt;数据加密、格式转换、统一编码&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;会话层&lt;/td&gt;
          &lt;td&gt;（合并到应用层）&lt;/td&gt;
          &lt;td&gt;（合并到应用层）&lt;/td&gt;
          &lt;td&gt;建立和维持应用之间的会话连接&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;传输层&lt;/td&gt;
          &lt;td&gt;传输层&lt;/td&gt;
          &lt;td&gt;传输层&lt;/td&gt;
          &lt;td&gt;给数据标端口，控制端到端的传输方式&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;网络层&lt;/td&gt;
          &lt;td&gt;网络层&lt;/td&gt;
          &lt;td&gt;网际层&lt;/td&gt;
          &lt;td&gt;给数据标 IP，规划端到端的传输路线&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据链路层&lt;/td&gt;
          &lt;td&gt;数据链路层&lt;/td&gt;
          &lt;td&gt;网络接口层&lt;/td&gt;
          &lt;td&gt;给数据标 MAC 地址，相邻节点之间的传输&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;物理层&lt;/td&gt;
          &lt;td&gt;物理层&lt;/td&gt;
          &lt;td&gt;（合并到网络接口层）&lt;/td&gt;
          &lt;td&gt;电信号、光信号的物理传输&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;7层osi参考模型&#34;&gt;7层OSI参考模型
&lt;/h3&gt;&lt;h4 id=&#34;7-应用层&#34;&gt;7. 应用层
&lt;/h4&gt;&lt;p&gt;应用层主要是给用户/应用提供可操作的服务，定义数据的业务含义。&lt;/p&gt;
&lt;p&gt;统称为 消息/报文 (Message)。&lt;/p&gt;
&lt;p&gt;我们写代码天天接触的HTTP、HTTPS、DNS、FTP、WebSocket、RPC协议，全在这一层。&lt;/p&gt;
&lt;p&gt;浏览器发请求、APP调接口，都是在应用层完成的。&lt;/p&gt;
&lt;h4 id=&#34;6-表示层&#34;&gt;6. 表示层
&lt;/h4&gt;&lt;p&gt;表示层主要是统一数据格式，保证收发双方能看懂对方的数据，核心负责数据的加密解密、格式转换、压缩解压、编码统一。&lt;/p&gt;
&lt;p&gt;理论上 OSI 模型把加密解密放在表示层；&lt;/p&gt;
&lt;p&gt;但在实际 TCP/IP 模型中，TLS/SSL 属于应用层实现，工作在应用层与传输层之间。&lt;/p&gt;
&lt;h4 id=&#34;5-会话层&#34;&gt;5. 会话层
&lt;/h4&gt;&lt;p&gt;会话层主要是管理应用之间的会话，保证数据不会串线。&lt;/p&gt;
&lt;p&gt;负责建立、维持、有序断开应用之间的会话连接，提供会话同步、全双工 / 半双工切换等管理能力；&lt;/p&gt;
&lt;p&gt;该层仅存在于 OSI 理论模型中，实际 TCP/IP 体系中无独立对应层，相关会话逻辑由应用层 + 传输层共同实现。&lt;/p&gt;
&lt;h4 id=&#34;4-传输层&#34;&gt;4. 传输层
&lt;/h4&gt;&lt;p&gt;传输层是端到端的传输控制，保证数据能准确送到对应的应用程序。&lt;/p&gt;
&lt;p&gt;数据单位称为 段 (Segment)。&lt;/p&gt;
&lt;p&gt;TCP（可靠传输）、UDP（不可靠传输），还有端口号（HTTP的80端口、HTTPS的443端口），都在这一层。&lt;/p&gt;
&lt;h4 id=&#34;3-网络层&#34;&gt;3. 网络层
&lt;/h4&gt;&lt;p&gt;作用是网络寻址和路由选择，保证数据能从源主机送到目标主机。&lt;/p&gt;
&lt;p&gt;数据单位称为 包 (Packet) 或 分组。&lt;/p&gt;
&lt;p&gt;IP 协议（IPv4/IPv6）、ARP（IP 转 MAC 地址）、ICMP（ping/tracert 命令）、OSPF/RIP 路由协议，都在这一层。&lt;/p&gt;
&lt;h4 id=&#34;2-数据链路层&#34;&gt;2. 数据链路层
&lt;/h4&gt;&lt;p&gt;相邻节点之间的可靠传输，负责成帧 (Framing)、差错检测和流量控制。&lt;/p&gt;
&lt;p&gt;数据单位称为 帧 (Frame)。&lt;br&gt;
MAC地址、以太网协议、交换机转发，都在这一层。&lt;/p&gt;
&lt;p&gt;给 IP 数据包加上 MAC 头部，标上源 MAC 和下一跳的 MAC 地址，保证相邻节点之间能准确传输。&lt;/p&gt;
&lt;h4 id=&#34;1-物理层&#34;&gt;1. 物理层
&lt;/h4&gt;&lt;p&gt;物理介质上的比特流传输。只负责传输0和1的比特流，不管数据是什么含义。&lt;/p&gt;
&lt;p&gt;数据单位称为 比特 (Bit)。&lt;/p&gt;
&lt;p&gt;网线、光纤、网卡、集线器、电信号、光信号，都在这一层。&lt;/p&gt;
&lt;h3 id=&#34;4层tcpip模型&#34;&gt;4层TCP/IP模型
&lt;/h3&gt;&lt;p&gt;OSI模型太理想化、太复杂，实际互联网落地的时候，做了两层合并，就成了现在真正在用的4层模型：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;应用层&lt;/strong&gt;：合并了OSI的&lt;strong&gt;应用层&lt;/strong&gt;+&lt;strong&gt;表示层&lt;/strong&gt;+&lt;strong&gt;会话层&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传输层&lt;/strong&gt;：同OSI的传输层&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网际层&lt;/strong&gt;：同OSI的网络层&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络接口层&lt;/strong&gt;：合并了OSI的&lt;strong&gt;数据链路层&lt;/strong&gt;+&lt;strong&gt;物理层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们现在上网、写代码的所有网络请求，全是跑在这个4层TCP/IP模型上的，是真正的工业标准。&lt;/p&gt;
&lt;h3 id=&#34;5层教材简化模型&#34;&gt;5层教材简化模型
&lt;/h3&gt;&lt;p&gt;国内教材为了兼顾教学易懂性和底层逻辑完整性，以 OSI 7 层模型为基础做简化，保留应用层、传输层、网络层，同时将底层拆分为数据链路层 + 物理层，形成了教学通用的 5 层简化模型。&lt;/p&gt;
&lt;p&gt;这个模型既保留了底层的物理传输逻辑，又简化了上层的复杂分层。&lt;/p&gt;
&lt;h3 id=&#34;举个例子&#34;&gt;举个例子
&lt;/h3&gt;&lt;p&gt;在浏览器输入网址，到页面显示出来，经过了网络模型的哪些层？&lt;/p&gt;
&lt;h4 id=&#34;1客户端发送请求从上到下封装数据包&#34;&gt;(1)客户端发送请求（从上到下封装数据包）
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;应用层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;浏览器解析网址，先通过 DNS 协议解析域名，拿到服务器的 IP 地址，再生成对应的 HTTP 请求。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;传输层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;先通过三次握手与服务器建立 TCP 连接，再给 HTTP 请求加上 TCP 头部，标上源端口和目标端口，完成传输层封装。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;网络层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;给TCP数据包加上IP头部，标上源IP和目标IP，路由器根据IP地址规划路由，找到服务器的网络地址。&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;&lt;strong&gt;数据链路层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;给IP数据包加上MAC头部，标上源MAC和下一跳的MAC地址，交换机根据MAC地址完成相邻节点转发。&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;&lt;strong&gt;物理层&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把数据包转换成电信号/光信号，通过网线、光纤、基站等物理介质，传输到目标服务器。&lt;/p&gt;
&lt;h4 id=&#34;2服务器接收并处理请求从下到上解析数据包&#34;&gt;(2)服务器接收并处理请求（从下到上解析数据包）
&lt;/h4&gt;&lt;p&gt;服务器收到数据后，从下到上逐层解包：物理层➡️数据链路层➡️网络层➡️传输层➡️应用层，最终解析 HTTP 请求、执行业务逻辑并返回响应。&lt;/p&gt;
&lt;h4 id=&#34;3-客户端渲染页面&#34;&gt;(3) 客户端渲染页面
&lt;/h4&gt;&lt;p&gt;浏览器收到响应后，解析HTML/CSS/JS，渲染页面并显示给用户。&lt;/p&gt;
&lt;h3 id=&#34;每层对应的协议&#34;&gt;每层对应的协议
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;层级名称&lt;/th&gt;
          &lt;th&gt;协议&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;应用层&lt;/td&gt;
          &lt;td&gt;HTTP、HTTPS、DNS、FTP、SMTP（邮件发送）、POP3/IMAP（邮件接收）、WebSocket、SSH、Dubbo 等 RPC 协议&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;传输层&lt;/td&gt;
          &lt;td&gt;TCP（可靠传输）、UDP（不可靠传输）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;网络层&lt;/td&gt;
          &lt;td&gt;IPv4、IPv6、ARP（IP 转 MAC 地址）、ICMP（ping/tracert）、OSPF、RIP 等路由协议&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据链路层&lt;/td&gt;
          &lt;td&gt;以太网协议、PPP 点对点协议、VLAN 协议、MAC 地址规范&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;物理层&lt;/td&gt;
          &lt;td&gt;无专属网络协议，相关规范：网线、光纤、网卡、集线器、RJ45 接口&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;小贴士：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ARP 协议逻辑上属于网络层（核心为 IP 协议提供地址解析服务，是 TCP/IP 网际层的核心配套协议），仅从报文封装形式来看，它被直接封装在以太网帧中，因此也有观点认为它属于数据链路层。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】Spring事务机制</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3spring%E4%BA%8B%E5%8A%A1%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Fri, 20 Mar 2026 09:06:26 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3spring%E4%BA%8B%E5%8A%A1%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;p&gt;之前面试的时候还真的有一个面试官问过我：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;什么是事务？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;事务，说人话就是：&lt;/p&gt;
&lt;p&gt;把&lt;strong&gt;多个数据库操作&lt;/strong&gt;打包成一个“不可分割的整体”，要么全成功，要么全失败并回滚到最初状态，绝对不会出现“只做了一半”的情况。&lt;/p&gt;
&lt;p&gt;以生活中最经典的&lt;strong&gt;银行转账&lt;/strong&gt;举个例子那是再合适不过了。&lt;/p&gt;
&lt;p&gt;且看：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A给B转100块钱，这一笔转账业务，其实是由两步独立的数据库操作组成的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A的账户扣100块&lt;/li&gt;
&lt;li&gt;B的账户加100块&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这两步必须同时成功、一起生效。&lt;/p&gt;
&lt;p&gt;如果A扣了钱B没收到，或者B收到了钱A没扣，那银行就乱套了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;事务的四大特性&#34;&gt;事务的四大特性
&lt;/h2&gt;&lt;p&gt;用银行转账的例子加深理解：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;特性&lt;/th&gt;
          &lt;th&gt;解释&lt;/th&gt;
          &lt;th&gt;银行转账例子&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;原子性（Atomicity）&lt;/td&gt;
          &lt;td&gt;要么全做，要么全不做，就像“原子”一样不可分割&lt;/td&gt;
          &lt;td&gt;A扣钱和B加钱必须同时成功，不能A扣了B没加&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;一致性（Consistency）&lt;/td&gt;
          &lt;td&gt;事务前后，数据要保持“一致”，总金额不变&lt;/td&gt;
          &lt;td&gt;转账前A有1000，B有0，总和是1000；转账后A有900，B有100，总和还是1000&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;隔离性（Isolation）&lt;/td&gt;
          &lt;td&gt;多个事务同时执行时，互相不干扰，就像“隔离开”一样&lt;/td&gt;
          &lt;td&gt;两个转账操作同时改A的账户，不能互相影响，不然钱就不对了&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;持久性（Durability）&lt;/td&gt;
          &lt;td&gt;事务提交后，数据就永久保存了，就算数据库挂了也不会丢&lt;/td&gt;
          &lt;td&gt;转账成功后，就算银行服务器断电重启，A和B的钱数也不会变&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;隔离级别&#34;&gt;隔离级别
&lt;/h2&gt;&lt;p&gt;为什么要有隔离级别呢？&lt;/p&gt;
&lt;p&gt;如果多个事务同时执行，没有隔离的话，会出现3个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;脏读&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;脏读就是读到了别人&lt;strong&gt;还没提交&lt;/strong&gt;的数据。&lt;/p&gt;
&lt;p&gt;比如A转给B100块，还没提交，B就查到自己多了100块，结果A回滚了，B白高兴一场。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/d764b2ecfe941769cf85903a7f204dbf-21873313.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;不可重复读&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不可重复读的意思是同一个事务里，&lt;strong&gt;两次读同一条数据的结果是不一样的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;比如A查自己有1000块，中间B转给了A100块提交了，A再查就变成1100了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/409e375f4bbb34cc711e1507612f5f4d-1292b6d6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;幻读&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;幻读就是同一个事务里，&lt;strong&gt;两次查的数量不一样。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;比如A查账户里有10条记录，中间B插了一条提交了，A再查就变成11条了，就像“幻觉”一样。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/3ac7b1e02df8fc8c0ce668e5c02750dd-e18fefb7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;这么看，&lt;strong&gt;不可重复读&lt;/strong&gt;和&lt;strong&gt;幻读&lt;/strong&gt;是由别的事务提交后导致的错误，而&lt;strong&gt;脏读&lt;/strong&gt;是读到了别人还没提交的临时数据。&lt;/p&gt;
&lt;p&gt;那不可重复读和幻读两者的区别在哪呢？&lt;/p&gt;
&lt;p&gt;不可重复读是盯着一条记录看，发现这条记录的内容变了。&lt;/p&gt;
&lt;p&gt;而幻读是盯着一堆记录数看，发现总数量变了。&lt;/p&gt;
&lt;p&gt;类比一下就是不可重复读是你看自己的银行卡余额，钱数变了；而幻读是你数银行有多少张卡，卡的数量变了。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;隔离级别&lt;/th&gt;
          &lt;th&gt;解决的问题&lt;/th&gt;
          &lt;th&gt;解释&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;读未提交（Read Uncommitted）&lt;/td&gt;
          &lt;td&gt;啥都解决不了&lt;/td&gt;
          &lt;td&gt;能读到别人没提交的数据，&lt;strong&gt;脏读、不可重复读、幻读&lt;/strong&gt;都有，一般不用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;读已提交（Read Committed）&lt;/td&gt;
          &lt;td&gt;脏读&lt;/td&gt;
          &lt;td&gt;只能读到别人&lt;strong&gt;提交后&lt;/strong&gt;的数据，但会有&lt;strong&gt;不可重复读、幻读&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;可重复读（Repeatable Read）&lt;/td&gt;
          &lt;td&gt;脏读、不可重复读&lt;/td&gt;
          &lt;td&gt;Spring默认的隔离级别。&lt;br/&gt;同一个事务里，&lt;strong&gt;两次读同一个数据一样&lt;/strong&gt;，但会有&lt;strong&gt;幻读&lt;/strong&gt;（MySQL InnoDB用MVCC解决了幻读）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;串行化（Serializable）&lt;/td&gt;
          &lt;td&gt;所有问题&lt;/td&gt;
          &lt;td&gt;事务一个接一个执行，就像“排队”一样，性能最差，一般不用&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;传播行为&#34;&gt;传播行为
&lt;/h2&gt;&lt;p&gt;传播行为就是：&lt;/p&gt;
&lt;p&gt;当一个事务方法调用另一个事务方法时，这两个事务该怎么协作？是直接加入原事务？还是新建一个独立的事务？还是先挂起原事务，等新事务执行完再继续？&lt;/p&gt;
&lt;p&gt;以下是3 个常用的传播行为:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;传播行为&lt;/th&gt;
          &lt;th&gt;解释&lt;/th&gt;
          &lt;th&gt;适用场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;REQUIRED（默认）&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;有事务就加入，没有就新建一个事务&lt;/td&gt;
          &lt;td&gt;最常用，比如转账服务&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;REQUIRES_NEW&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;新建一个事务，挂起原事务，等新事务执行完再继续原事务&lt;/td&gt;
          &lt;td&gt;比如记录日志，不管主事务成功失败，日志都要记录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;NESTED&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;嵌套事务，有一个保存点：&lt;br/&gt;1. 子事务回滚：只回滚自己，不影响父事务和其他子事务&lt;br/&gt;2. 父事务回滚：带着所有子事务一起回滚&lt;/td&gt;
          &lt;td&gt;系统批量发福利：某一个用户的福利发放失败，不影响其他用户；但整体回滚，所有用户的福利都会被收回&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;小贴士&#34;&gt;小贴士
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Spring AOP 是基于代理的，&lt;code&gt;@Transactional &lt;/code&gt;只能用在 public 方法上，不然不生效。不管是 JDK 动态代理还是 CGLIB 代理，Spring 都只保证 public 方法的事务生效，非 public 的写了也白写。&lt;/li&gt;
&lt;li&gt;内部调用不会走代理类，直接调用&lt;code&gt;this.method()&lt;/code&gt;，所以，同一个类里方法调用，&lt;code&gt;@Transactional&lt;/code&gt;不生效。&lt;/li&gt;
&lt;li&gt;Spring默认只回滚&lt;strong&gt;非受检异常&lt;/strong&gt;（RuntimeException、Error），&lt;strong&gt;受检异常&lt;/strong&gt;（Exception）不回滚。如果要所有异常都回滚，需要在 &lt;code&gt;@Transactional&lt;/code&gt; 里加&lt;code&gt;rollbackFor = Exception.class&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;MySQL的MyISAM引擎不支持事务，只有InnoDB支持。&lt;/li&gt;
&lt;/ol&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】电商高并发场景下商品详情页系统设计</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E7%94%B5%E5%95%86%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E5%95%86%E5%93%81%E8%AF%A6%E6%83%85%E9%A1%B5%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</link>
        <pubDate>Wed, 18 Mar 2026 12:32:01 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E7%94%B5%E5%95%86%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E5%95%86%E5%93%81%E8%AF%A6%E6%83%85%E9%A1%B5%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid>
        <description>&lt;p&gt;很多面试官早就对“Redis缓存、MySQL分库分表”这些东西形成免疫了，他们更想要听到的是一个完整的链路，比如&lt;strong&gt;从服务到数据、从接入到落地&lt;/strong&gt;的全流程设计。&lt;/p&gt;
&lt;p&gt;当时面试官问我：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一家电商公司，它主要是面向消费者的，公司的一些业务场景流量会很高，比如说展示商品的那个页面，它的流量就很高，假设现在由你来做这个系统，负责商品详情页的展示，你能从服务的层面，数据的层面等多个维度来设计一下，让它能够支持一个更高的查询能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;场景梳理&#34;&gt;场景梳理
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;业务背景&lt;/strong&gt;&lt;br&gt;
电商商品详情页，面对&lt;strong&gt;百万级QPS&lt;/strong&gt;的高并发流量（比如大促、爆款商品）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;需求分析&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;性能&lt;/strong&gt;：支持高并发查询，响应时间 &amp;lt; 100ms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一致性&lt;/strong&gt;：商品价格、库存、标题修改后，能及时更新&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高可用&lt;/strong&gt;：服务挂了、Redis挂了，系统不崩&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展&lt;/strong&gt;：新增商品、新增规格，能快速接入&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;关键数据&lt;/strong&gt;&lt;br&gt;
商品基本信息（标题、主图、详情图）、价格（实时/活动）、库存、规格参数、促销信息（优惠券/秒杀）、评价统计。&lt;/p&gt;
&lt;h2 id=&#34;设计思路&#34;&gt;设计思路
&lt;/h2&gt;&lt;p&gt;以“多级缓存”为核心，配合“服务分层+数据分层+异步更新”，构建“读多写少”的高并发详情页系统。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统架构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/80f409a26c57e173eafe7fbf8860a039-6716bb00.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;画板&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;服务层面&#34;&gt;服务层面
&lt;/h3&gt;&lt;p&gt;采用&lt;strong&gt;前后端分离+网关层+服务分层&lt;/strong&gt;架构：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;层级&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;CDN层&lt;/td&gt;
          &lt;td&gt;静态资源加速（图片、js、css、详情页HTML）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;网关层&lt;/td&gt;
          &lt;td&gt;路由、限流、鉴权、接口聚合&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;API网关聚合层&lt;/td&gt;
          &lt;td&gt;聚合多个微服务接口（比如商品基础信息、价格、库存、评价），减少请求次数&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;商品服务层&lt;/td&gt;
          &lt;td&gt;核心业务逻辑（查商品、查库存、查价格）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据服务层&lt;/td&gt;
          &lt;td&gt;专门做数据查询（缓存+数据库）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;商品详情页是典型的&lt;strong&gt;读多写少&lt;/strong&gt;场景，考虑&lt;strong&gt;服务层本地缓存 + 分布式缓存。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;采用&lt;strong&gt;Caffeine&lt;/strong&gt;作为服务层本地缓存，缓存&lt;strong&gt;极热点商品&lt;/strong&gt;（比如爆款商品），避免每次请求都走Redis，&lt;/p&gt;
&lt;p&gt;采用&lt;strong&gt;Redis&lt;/strong&gt;作为分布式缓存，作为&lt;strong&gt;核心缓存层&lt;/strong&gt;，缓存所有商品的详情数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;调用流程&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;先查本地缓存（Caffeine），如果命中就直接返回；&lt;/p&gt;
&lt;p&gt;如果本地未命中，查 Redis，命中则写入本地缓存，返回；&lt;/p&gt;
&lt;p&gt;如果Redis 未命中，查数据库，查到则写入 Redis + 本地缓存，返回。&lt;/p&gt;
&lt;h3 id=&#34;数据层面&#34;&gt;数据层面
&lt;/h3&gt;&lt;p&gt;商品数据分为&lt;strong&gt;3类&lt;/strong&gt;，不同类型用不同策略：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;数据类型&lt;/th&gt;
          &lt;th&gt;例子&lt;/th&gt;
          &lt;th&gt;缓存策略&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;静态数据&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;商品标题、主图、详情图、规格参数&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;永久缓存（不过期）&lt;/strong&gt;，更新时主动删除&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;半动态数据&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;商品价格、促销信息（优惠券、满减）&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;短期缓存（5-10分钟）&lt;/strong&gt;，更新时主动删除&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;动态数据&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;库存（实时）、实时销量&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;单独缓存&lt;/strong&gt;，库存用Redis原子操作；实时销量异步更新&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;缓存更新的原则&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;为了避免高并发下的缓存不一致问题，采用的是先更新数据库，再删除缓存的方式。&lt;/p&gt;
&lt;h3 id=&#34;高并发优化&#34;&gt;高并发优化
&lt;/h3&gt;&lt;p&gt;1.热点数据预热&lt;/p&gt;
&lt;p&gt;大促前，把热门商品（如销量TOP100）写入Redis和本地缓存。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;限流+熔断+降级&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对商品详情接口设置限流。&lt;/p&gt;
&lt;p&gt;考虑使用&lt;strong&gt;服务熔断&lt;/strong&gt;，当Redis/数据库不可用时，快速失败，避免系统雪崩。&lt;/p&gt;
&lt;p&gt;或者考虑使用&lt;strong&gt;降级策略&lt;/strong&gt;，当Redis挂了，降级到直接查数据库，如果数据库也挂了，降级返回默认商品信息。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;页面静态化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对于完全静态的商品（比如虚拟商品或者不参与促销的商品），生成&lt;strong&gt;静态HTML页面&lt;/strong&gt;，直接存到CDN，这样的话用户请求直接由CDN返回，不经过任何服务。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】高并发场景下“恶意登录设备识别”系统设计</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E6%81%B6%E6%84%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E5%A4%87%E8%AF%86%E5%88%AB%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</link>
        <pubDate>Wed, 18 Mar 2026 10:41:34 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3%E9%AB%98%E5%B9%B6%E5%8F%91%E5%9C%BA%E6%99%AF%E4%B8%8B%E6%81%B6%E6%84%8F%E7%99%BB%E5%BD%95%E8%AE%BE%E5%A4%87%E8%AF%86%E5%88%AB%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid>
        <description>&lt;p&gt;这是面试遇到的一道场景题。&lt;/p&gt;
&lt;p&gt;面试官问我：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有这么一个场景，现在有一个网站，这个网站访问的人非常多，在登录的时候有些坏人来这里捣乱，怎么去设计一个系统实现把这些坏人找到的逻辑。&lt;/p&gt;
&lt;p&gt;条件说一下：&lt;/p&gt;
&lt;p&gt;登录的时候你要的用户userid，时间 ，设备deviceid这些信息都有；&lt;/p&gt;
&lt;p&gt;坏人的定义是：最近十分钟之内，一个设备上登录了大于等于5个user，我就认为这个是坏人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;场景梳理&#34;&gt;场景梳理
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;输入信息&lt;/strong&gt;：用户登录时的 &lt;code&gt;userId&lt;/code&gt;（用户ID）、&lt;code&gt;timestamp&lt;/code&gt;（登录时间戳）、&lt;code&gt;deviceId&lt;/code&gt;（设备ID）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;坏人定义&lt;/strong&gt;：最近10分钟（600秒）内，同一个deviceId上登录了≥5个不同的userId；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输出结果&lt;/strong&gt;：标记该deviceId为“恶意设备”，后续可以做拦截、验证码、风控等处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;实现方案&#34;&gt;实现方案
&lt;/h2&gt;&lt;p&gt;考虑用Redis ZSet实现滑动窗口。&lt;/p&gt;
&lt;h3 id=&#34;为什么选redis&#34;&gt;为什么选Redis？
&lt;/h3&gt;&lt;p&gt;主要是因为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;单线程高性能，适合高并发场景&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内存数据库，读写速度极快&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持丰富的数据结构（比如这次场景需要的ZSet）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持过期时间，自动清理数据&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;为什么选zset&#34;&gt;为什么选ZSet？
&lt;/h3&gt;&lt;p&gt;ZSet（有序集合）是 Redis 中最适合做 “时间窗口、排行榜、去重 + 排序” 的数据结构。&lt;/p&gt;
&lt;p&gt;它有以下几个特点：&lt;br&gt;
Member（成员）唯一，Member 不能重复，天然去重；&lt;br&gt;
每个 Member 对应一个 Score（分数），Score 是一个浮点数，用来排序；&lt;br&gt;
ZSet 会自动按 Score 从小到大进行排序，如果Score 相同则按 Member 字典序排序。&lt;/p&gt;
&lt;p&gt;ZSet 的底层是哈希表 + 跳表，哈希表保证 Member 唯一、O (1) 查找，跳表保证有序、O (log n) 范围查询。&lt;/p&gt;
&lt;h3 id=&#34;数据模型&#34;&gt;数据模型
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;维度&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Key&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;login:device:{deviceId}&lt;/code&gt;（比如 &lt;code&gt;login:device:abc123&lt;/code&gt;）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Value（ZSet Member）&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;userId&lt;/code&gt;（用户ID，保证唯一性，去重）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Score（ZSet Score）&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;登录时间戳&lt;/code&gt;（用于滑动窗口）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;理一下整个流程&#34;&gt;理一下整个流程
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;用户发起登录请求：携带 &lt;code&gt;userId&lt;/code&gt;、&lt;code&gt;deviceId&lt;/code&gt;、&lt;code&gt;timestamp&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;先查黑名单：如果已经是恶意设备，直接拦截；&lt;/li&gt;
&lt;li&gt;写入登录记录到Redis ZSet：以 &lt;code&gt;deviceId&lt;/code&gt; 为Key，&lt;code&gt;userId&lt;/code&gt; 为Member，&lt;code&gt;timestamp&lt;/code&gt; 为Score，写入ZSet；&lt;/li&gt;
&lt;li&gt;清理过期数据：删除ZSet中Score &amp;lt; 当前时间 - 10分钟的元素；&lt;/li&gt;
&lt;li&gt;统计最近10分钟内的不同userId数量：用 &lt;code&gt;ZCard&lt;/code&gt; 命令统计ZSet的元素个数；&lt;/li&gt;
&lt;li&gt;判断是否为坏人：如果数量≥5，标记该deviceId为恶意设备，存入Redis黑名单；&lt;/li&gt;
&lt;li&gt;返回登录结果：如果是恶意设备，返回“请完成验证码”或“登录失败”；否则正常登录。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;简化版的代码实现&#34;&gt;简化版的代码实现
&lt;/h3&gt;&lt;p&gt;用Spring Boot + RedisTemplate写一个简化版的实现，加深理解一下：&lt;/p&gt;
&lt;h4 id=&#34;1先定义redis-key的前缀和常量&#34;&gt;（1）先定义Redis Key的前缀和常量
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 设备登录记录Key前缀&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LOGIN_DEVICE_KEY_PREFIX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;login:device:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 恶意设备黑名单Key前缀&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MALICIOUS_DEVICE_KEY_PREFIX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;malicious:device:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 滑动窗口大小：10分钟（600秒）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WINDOW_SIZE_SECONDS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;600&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 坏人阈值：≥5个不同userId&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MALICIOUS_THRESHOLD&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 过期时间：10分钟&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;final&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EXPIRE_TIME_SECONDS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;600&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;2服务实现&#34;&gt;（2）服务实现
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.data.redis.core.RedisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.stereotype.Service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.util.concurrent.TimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Service&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;LoginRiskControlService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisTemplate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 检查是否为恶意设备
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * @param userId 用户ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * @param deviceId 设备ID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * @return true-是恶意设备，false-正常设备
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;checkMaliciousDevice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deviceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 构造Redis Key&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loginKey&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;LOGIN_DEVICE_KEY_PREFIX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deviceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maliciousKey&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MALICIOUS_DEVICE_KEY_PREFIX&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deviceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 先查黑名单：如果已经是恶意设备，直接返回true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Boolean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;TRUE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;equals&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;hasKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maliciousKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 3. 获取当前时间戳（秒）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currentTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;currentTimeMillis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 计算窗口起始时间：当前时间 - 10分钟&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;windowStartTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currentTime&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;WINDOW_SIZE_SECONDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 4. 写入当前登录记录到ZSet：Member=userId，Score=当前时间戳&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;opsForZSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loginKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;currentTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 5. 清理过期数据：删除Score &amp;lt; 窗口起始时间的元素&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;opsForZSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;removeRangeByScore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loginKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;windowStartTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 6. 统计最近10分钟内的不同userId数量（ZSet的元素个数）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Long&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;opsForZSet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;zCard&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loginKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 7. 设置Key的过期时间：10分钟（防止内存泄漏）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;expire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loginKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;EXPIRE_TIME_SECONDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;SECONDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 8. 判断是否为坏人：数量≥5&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RedisKeyConstants&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;MALICIOUS_THRESHOLD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 标记为恶意设备，存入黑名单，过期时间可以设长一点（比如24小时）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redisTemplate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;opsForValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;maliciousKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TimeUnit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;HOURS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 9. 正常设备，返回false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;3在登录接口中调用&#34;&gt;（3）在登录接口中调用
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.web.bind.annotation.PostMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.web.bind.annotation.RequestMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;org.springframework.web.bind.annotation.RestController&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@RestController&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@RequestMapping&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/login&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;LoginController&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Autowired&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LoginRiskControlService&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riskControlService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@PostMapping&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;login&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deviceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 先检查是否为恶意设备&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isMalicious&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riskControlService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;checkMaliciousDevice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deviceId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isMalicious&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;登录失败：检测到异常设备，请完成验证码验证&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 正常登录逻辑（校验账号密码、生成Token等）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;登录成功&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;高并发场景的优化&#34;&gt;高并发场景的优化
&lt;/h3&gt;&lt;p&gt;在访问量非常大的场景下，上面的代码肯定扛不住，所以还需要做一些优化。&lt;/p&gt;
&lt;h4 id=&#34;1-布隆过滤器快速过滤正常设备&#34;&gt;1. 布隆过滤器快速过滤正常设备
&lt;/h4&gt;&lt;p&gt;如果每个登录请求都查Redis，Redis的压力会很大。&lt;/p&gt;
&lt;p&gt;可以考虑用&lt;strong&gt;布隆过滤器&lt;/strong&gt;先快速过滤：&lt;/p&gt;
&lt;p&gt;在布隆过滤器里只存“潜在恶意设备”（比如最近10分钟内登录过≥3个userId的设备），登录时先查布隆过滤器，如果不在里面，直接放行，不查Redis；如果在里面，再查Redis确认。&lt;/p&gt;
&lt;h4 id=&#34;2-异步处理&#34;&gt;2. 异步处理
&lt;/h4&gt;&lt;p&gt;如果Redis查询慢，会阻塞主登录流程，影响用户体验。&lt;/p&gt;
&lt;p&gt;考虑把“写入ZSet、清理过期数据、统计数量、判断坏人”这些步骤用线程池或消息队列&lt;strong&gt;异步执行&lt;/strong&gt;，这样的话主登录流程只做“账号密码校验、生成Token”，用户体验会更好一点。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小贴士&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;异步会有极短的延迟，但是风控允许这种小误差。&lt;/p&gt;
&lt;h4 id=&#34;3-本地缓存redis双层缓存&#34;&gt;3. 本地缓存+Redis双层缓存
&lt;/h4&gt;&lt;p&gt;如果恶意设备黑名单查询很频繁，每次都查Redis还是会有压力。&lt;/p&gt;
&lt;p&gt;考虑用&lt;strong&gt;本地缓存（比如Caffeine）+ Redis&lt;/strong&gt; 的双层缓存。&lt;/p&gt;
&lt;p&gt;这样一来本地缓存存“最近1分钟内的恶意设备黑名单”，登录时先查本地缓存，命中直接返回，没命中再查Redis，查到后写入本地缓存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小贴士：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本地缓存不能设 10 分钟，因为多实例部署时，本地缓存互不同步，会导致风控失效。&lt;/p&gt;
&lt;p&gt;设 1 分钟既能减轻 Redis 压力，又能保证安全可控。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】Spring中的注解</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3spring%E4%B8%AD%E7%9A%84%E6%B3%A8%E8%A7%A3/</link>
        <pubDate>Wed, 18 Mar 2026 08:53:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3spring%E4%B8%AD%E7%9A%84%E6%B3%A8%E8%A7%A3/</guid>
        <description>&lt;p&gt;面试官没有直接问我“@Autowired和@Resource的区别”这种题，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;先让我举几个Spring注解的例子，再问“Spring注解定义为什么都挺短，不会有大量代码”，直接考察你对注解本质和Spring AOP/IOC底层的理解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;举几个spring注解的例子&#34;&gt;举几个Spring注解的例子
&lt;/h2&gt;&lt;h3 id=&#34;1-ioc依赖注入类注解&#34;&gt;1. IOC依赖注入类注解
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;注解&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
          &lt;th&gt;区别&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@Autowired&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;Spring原生注解，按&lt;strong&gt;类型（byType）&lt;/strong&gt; 自动注入依赖&lt;/td&gt;
          &lt;td&gt;优先byType，找不到唯一Bean时，会尝试按&lt;strong&gt;变量名（byName）&lt;/strong&gt; 匹配；可以配合 &lt;code&gt;@Qualifier&lt;/code&gt; 指定Bean名称；可以用 &lt;code&gt;required=false&lt;/code&gt; 允许依赖为空&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@Qualifier&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;配合 &lt;code&gt;@Autowired&lt;/code&gt; 使用，&lt;strong&gt;强制按名称（byName）&lt;/strong&gt; 注入依赖&lt;/td&gt;
          &lt;td&gt;当容器中有多个同类型Bean时，必须用它指定具体用哪个&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@Resource&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;按&lt;strong&gt;名称（byName）&lt;/strong&gt; 优先注入，找不到再按&lt;strong&gt;类型（byType）&lt;/strong&gt; 匹配&lt;/td&gt;
          &lt;td&gt;JSR-250标准，跨框架兼容性好；可以用 &lt;code&gt;name&lt;/code&gt; 属性显式指定Bean名称&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;2-ioc组件注册类注解&#34;&gt;2. IOC组件注册类注解
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;注解&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;@Component&lt;/td&gt;
          &lt;td&gt;通用组件注册注解，把任意类标记为Spring容器的Bean&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Controller&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;@Component&lt;/code&gt; 的子类，专门标记&lt;strong&gt;Web控制器层&lt;/strong&gt;的Bean&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Service&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;@Component&lt;/code&gt; 的子类，专门标记&lt;strong&gt;业务逻辑层&lt;/strong&gt;的Bean&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Configuration&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;@Component&lt;/code&gt; 的子类，专门标记&lt;strong&gt;Java配置类&lt;/strong&gt;，替代XML配置文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Repository&lt;/td&gt;
          &lt;td&gt;@Component 的子类，专门标记数据访问层的 Bean；还能把数据库的异常（如 SQLException）转换成 Spring 的 DataAccessException（非受检异常）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;3-aop切面类注解&#34;&gt;3. AOP切面类注解
&lt;/h3&gt;&lt;p&gt;横切关注点就是“和业务逻辑无关，但很多地方都要用的功能”，比如日志、事务、权限校验、性能监控这些。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;注解&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;@Aspect&lt;/td&gt;
          &lt;td&gt;把类标记为&lt;strong&gt;切面类&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Pointcut&lt;/td&gt;
          &lt;td&gt;定义&lt;strong&gt;切入点表达式&lt;/strong&gt;，告诉Spring“哪些方法需要被增强”&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Before&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;前置通知&lt;/strong&gt;：在目标方法执行&lt;strong&gt;之前&lt;/strong&gt;执行增强逻辑&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@After&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;后置通知&lt;/strong&gt;：在目标方法执行&lt;strong&gt;之后&lt;/strong&gt;（不管有没有异常）执行增强逻辑&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@AfterReturning&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;返回通知&lt;/strong&gt;：在目标方法&lt;strong&gt;正常返回&lt;/strong&gt;之后执行增强逻辑&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@AfterThrowing&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;异常通知&lt;/strong&gt;：在目标方法&lt;strong&gt;抛出异常&lt;/strong&gt;之后执行增强逻辑&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;@Around&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;环绕通知&lt;/strong&gt;：最强大的通知，可以&lt;strong&gt;完全控制目标方法的执行&lt;/strong&gt;（比如不执行目标方法、修改参数、修改返回值）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;lombok的常用注解&#34;&gt;Lombok的常用注解
&lt;/h3&gt;&lt;p&gt;Lombok是一种代码简化工具，通过注解自动生成代码。&lt;/p&gt;
&lt;p&gt;虽然不是Spring原生注解，但Lombok的注解是Java开发中经常会用到的。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;注解&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@Slf4j&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;自动生成 &lt;strong&gt;SLF4J日志对象&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@Data&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;自动生成 &lt;strong&gt;getter/setter、toString、equals、hashCode、无参构造器&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@NoArgsConstructor&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;自动生成 &lt;strong&gt;无参构造器&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;@AllArgsConstructor&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;自动生成 &lt;strong&gt;全参构造器&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;spring-注解定义为什么通常都挺短不会有大量代码&#34;&gt;Spring 注解定义为什么通常都挺短，不会有大量代码？
&lt;/h2&gt;&lt;p&gt;面试官这么问主要是想考察你对注解本质和Spring容器底层的理解。&lt;/p&gt;
&lt;p&gt;先明确一下&lt;strong&gt;元数据&lt;/strong&gt;（Metadata） 的定义，元数据就是“描述数据的数据”，本身不是数据的内容，只是给数据打标签、加属性。&lt;/p&gt;
&lt;p&gt;根本原因在于注解只是&lt;strong&gt;元数据标记&lt;/strong&gt;，本身不包含任何逻辑。&lt;/p&gt;
&lt;p&gt;举个Spring注解的例子，比如 &lt;code&gt;@Transactional&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// @Transactional的核心定义（简化版，去掉了很多元注解和属性）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElementType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;METHOD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElementType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;TYPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 元注解：标记这个注解能用在方法/类上&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Retention&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;RUNTIME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 元注解：标记这个注解保留到运行时&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Inherited&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 元注解：标记这个注解可以被子类继承&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Documented&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 元注解：标记这个注解会被生成到JavaDoc里&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@interface&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Transactional&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 只有属性，没有任何方法/逻辑！&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 事务管理器的名称&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Propagation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;propagation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Propagation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;REQUIRED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 传播行为&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Isolation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;isolation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Isolation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 隔离级别&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 超时时间&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;readOnly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 是否只读&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;可以看到&lt;code&gt;@Transactional&lt;/code&gt; 的定义里只有属性，没有任何一行业务逻辑或容器逻辑。&lt;/p&gt;
&lt;p&gt;它只是告诉Spring容器：“这个方法/类需要开启事务，传播行为是REQUIRED，隔离级别是DEFAULT&amp;hellip;”，真正开启/提交/回滚事务的逻辑，全在Spring的事务管理器和AOP切面里。&lt;/p&gt;
&lt;p&gt;除此之外，Spring注解的定义比较短，还有一个原因是Spring大量使用了元注解（Meta-Annotation）。&lt;/p&gt;
&lt;p&gt;那什么是元注解呢？&lt;/p&gt;
&lt;p&gt;元注解就是“标记注解的注解”，可以复用已有注解的功能，不用重复写代码。&lt;/p&gt;
&lt;p&gt;比如上面的 &lt;code&gt;@Transactional&lt;/code&gt;，它上面有&lt;code&gt;@Target&lt;/code&gt;、&lt;code&gt;@Retention&lt;/code&gt;、&lt;code&gt;@Inherited&lt;/code&gt;、&lt;code&gt;@Documented&lt;/code&gt;这4个元注解。&lt;/p&gt;
&lt;h2 id=&#34;怎么自定义一个spring注解&#34;&gt;怎么自定义一个Spring注解？
&lt;/h2&gt;&lt;p&gt;简单来说有以下3步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;定义注解&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用 &lt;code&gt;@interface&lt;/code&gt; 定义，加上JDK原生元注解（至少需要 &lt;code&gt;@Target&lt;/code&gt; 和 &lt;code&gt;@Retention(RUNTIME)&lt;/code&gt;）；&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;编写后置处理器&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;实现 &lt;code&gt;BeanPostProcessor&lt;/code&gt; 或 &lt;code&gt;BeanFactoryPostProcessor&lt;/code&gt; 接口，或者用 AOP 切面（更适合方法级别的拦截，比如权限校验、记录执行时间），处理自定义注解的逻辑；&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;注册后置处理器&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把自定义后置处理器交给Spring容器管理（比如用 &lt;code&gt;@Component&lt;/code&gt; 标记）。&lt;/p&gt;
&lt;p&gt;举个例子@AuthCheck：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ElementType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;METHOD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//规定这个标签能贴在哪里,ElementType.METHOD 意思是“只能贴在方法上面”。&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Retention&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RetentionPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;RUNTIME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//规定这个标签的有效期,RUNTIME的意思是“程序运行的时候，这个标签依然存在”&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@interface&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AuthCheck&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * @interface 不是接口（interface），而是专门用来定义注解的语法。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * String mustRole()：表示用这个标签时，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 你可以写上一个角色名，比如 @AuthCheck(mustRole = &amp;#34;admin&amp;#34;)。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * default &amp;#34;&amp;#34;：如果你只写了 @AuthCheck
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * 而没填角色，那默认值就是一个空字符串 &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * （表示不需要特定角色，只要登录就行，具体看你怎么实现业务逻辑）。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;mustRole&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>【面试真题拆解】Java文件操作的异常类型与受检_非受检异常</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3java%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C%E7%9A%84%E5%BC%82%E5%B8%B8%E7%B1%BB%E5%9E%8B%E4%B8%8E%E5%8F%97%E6%A3%80_%E9%9D%9E%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8/</link>
        <pubDate>Tue, 17 Mar 2026 21:09:31 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3java%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C%E7%9A%84%E5%BC%82%E5%B8%B8%E7%B1%BB%E5%9E%8B%E4%B8%8E%E5%8F%97%E6%A3%80_%E9%9D%9E%E5%8F%97%E6%A3%80%E5%BC%82%E5%B8%B8/</guid>
        <description>&lt;p&gt;以往面试的时候问Java异常好像背一背题就行，但是这个面试官水平还是很高的，换了一种问法，大概是这么问的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Java中写了一个函数，打开了一个文件，读这个文件然后关闭，中间没有写try-catch，保存之后，编辑器自动在这个函数声明后面加了throws exception，你觉得这些exception会有哪些呢？&lt;/p&gt;
&lt;p&gt;如果把这个函数进行修改，不读写文件，只进行简单的数据处理，这时保存之后，发现函数声明的后面没有throws这些异常了，在这种情况下是不是就不会抛出异常了？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有点懵了，完犊子。。。&lt;/p&gt;
&lt;p&gt;主要有以下几点，掌握了应该问题不大：&lt;/p&gt;
&lt;p&gt;文件操作的异常主要是&lt;strong&gt;IOException及其子类&lt;/strong&gt;，属于&lt;strong&gt;受检异常&lt;/strong&gt;，必须显式处理（要么 &lt;code&gt;try-catch&lt;/code&gt;，要么 &lt;code&gt;throws&lt;/code&gt;）；&lt;/p&gt;
&lt;p&gt;简单数据处理的异常多是&lt;strong&gt;RuntimeException 及其子类&lt;/strong&gt;（如空指针、数组越界），属于&lt;strong&gt;非受检异常&lt;/strong&gt;，无需强制 &lt;code&gt;throws&lt;/code&gt; 或 &lt;code&gt;try-catch&lt;/code&gt;；&lt;/p&gt;
&lt;p&gt;受检异常要求“编译时必须处理”，非受检异常是“运行时才可能出现”，编译器不强制要求。&lt;/p&gt;
&lt;h2 id=&#34;文件操作会抛出哪些异常&#34;&gt;文件操作会抛出哪些异常？
&lt;/h2&gt;&lt;p&gt;复现一下面试题里的场景：&lt;/p&gt;
&lt;p&gt;写一个文件读写函数，没加 &lt;code&gt;try-catch&lt;/code&gt;，编辑器自动加了 &lt;code&gt;throws Exception&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nn&#34;&gt;java.io.*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;FileReadExample&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 编辑器自动加了 throws Exception，因为里面有受检异常&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;readFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;throws&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 打开文件流&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileInputStream&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileInputStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 读取文件内容&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 3. 关闭文件流&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;有以下几种文件操作中常见的异常&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;异常类型&lt;/th&gt;
          &lt;th&gt;触发场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;FileNotFoundException&lt;/td&gt;
          &lt;td&gt;文件路径不存在、文件是目录、没有读权限&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;IOException&lt;/td&gt;
          &lt;td&gt;读取文件时IO错误（如磁盘损坏、网络文件系统（NFS）的连接意外中断）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;EOFException&lt;/td&gt;
          &lt;td&gt;读取到文件末尾仍尝试读取&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;小贴士&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;FileNotFoundException&lt;/code&gt; 是 &lt;code&gt;IOException&lt;/code&gt; 的子类，如果 &lt;code&gt;throws IOException&lt;/code&gt;，也能覆盖 &lt;code&gt;FileNotFoundException&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;为什么简单数据处理不需要-throws&#34;&gt;为什么简单数据处理不需要 &lt;code&gt;throws&lt;/code&gt;？
&lt;/h2&gt;&lt;p&gt;面试题里的第二个场景：&lt;/p&gt;
&lt;p&gt;把函数改成只做简单数据处理，编辑器就不加 &lt;code&gt;throws&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;复现一下这个场景：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SimpleDataProcessExample&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 编辑器不会加 throws，因为里面都是非受检异常&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;processData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 1. 数组越界：ArrayIndexOutOfBoundsException（非受检）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;num&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 2. 空指针：NullPointerException（非受检）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 3. 非法参数：IllegalArgumentException（非受检）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;数组不能为空&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里的主要原因是：&lt;/p&gt;
&lt;p&gt;简单数据处理的异常都是 &lt;code&gt;RuntimeException&lt;/code&gt; 的子类，是&lt;strong&gt;非受检异常&lt;/strong&gt;，编辑器不会强制要求 &lt;code&gt;throws&lt;/code&gt; 或 &lt;code&gt;try-catch&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;受检异常-vs-非受检异常&#34;&gt;受检异常 vs 非受检异常
&lt;/h2&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;对比维度&lt;/th&gt;
          &lt;th&gt;受检异常（Checked Exception）&lt;/th&gt;
          &lt;th&gt;非受检异常（Unchecked Exception）&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;定义&lt;/td&gt;
          &lt;td&gt;编译时必须处理的异常，不处理编译不通过&lt;/td&gt;
          &lt;td&gt;运行时才可能出现的异常，编译器不强制要求处理&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;所属类&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;Exception&lt;/code&gt; 及其子类（除了 &lt;code&gt;RuntimeException&lt;/code&gt; 及其子类）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;RuntimeException&lt;/code&gt; 及其子类、&lt;code&gt;Error&lt;/code&gt; 及其子类&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;典型例子&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;IOException&lt;/code&gt;、&lt;code&gt;FileNotFoundException&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;NullPointerException&lt;/code&gt;、&lt;code&gt;ArrayIndexOutOfBoundsException&lt;/code&gt;、&lt;code&gt;IllegalArgumentException&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;处理要求&lt;/td&gt;
          &lt;td&gt;必须 &lt;code&gt;try-catch&lt;/code&gt; 或 &lt;code&gt;throws&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;可以不处理，也可以处理&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;设计目的&lt;/td&gt;
          &lt;td&gt;提醒开发者“这里可能有外部不可控的错误，必须处理”&lt;/td&gt;
          &lt;td&gt;提醒开发者“这里有代码逻辑错误，需要修复代码”&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;实际开发中文件操作的异常怎么处理&#34;&gt;实际开发中，文件操作的异常怎么处理？
&lt;/h2&gt;&lt;p&gt;不要直接 &lt;code&gt;throws Exception&lt;/code&gt;，推荐显式抛出具体异常（如 &lt;code&gt;throws IOException&lt;/code&gt;）或者 &lt;code&gt;try-catch&lt;/code&gt; 处理，记录日志并给出友好提示。&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;readFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileInputStream&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileInputStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileNotFoundException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;文件不存在：&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;printStackTrace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IOException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;文件读取失败&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;printStackTrace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;finally&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 必须在 finally 里关闭流，防止内存泄漏&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;close&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IOException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;printStackTrace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>前后端分离&#43;微服务架构下的用户认证</title>
        <link>https://iamxurulin.github.io/p/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%8B%E7%9A%84%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81/</link>
        <pubDate>Tue, 17 Mar 2026 19:38:35 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%89%8D%E5%90%8E%E7%AB%AF%E5%88%86%E7%A6%BB-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E4%B8%8B%E7%9A%84%E7%94%A8%E6%88%B7%E8%AE%A4%E8%AF%81/</guid>
        <description>&lt;p&gt;先明确一些核心组件：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;组件&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;前端&lt;/td&gt;
          &lt;td&gt;页面展示、用户交互、存储Token&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;API网关&lt;/td&gt;
          &lt;td&gt;统一入口、路由转发、Token校验&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;认证中心&lt;/td&gt;
          &lt;td&gt;用户登录验证、生成/刷新Token&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;微服务&lt;/td&gt;
          &lt;td&gt;处理业务逻辑、获取用户信息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据库&lt;/td&gt;
          &lt;td&gt;存储用户账号密码、权限信息&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;当时面试官跟我说，你可以从前端打开一个页面开始讲。&lt;/p&gt;
&lt;p&gt;那我们就举个例子：&lt;/p&gt;
&lt;p&gt;以用户打开一个电商网站首页➡️跳转登录➡️输入账号密码登录➡️携带Token请求商品列表为例，说一下这个完整的流程。&lt;/p&gt;
&lt;h3 id=&#34;1前端打开页面检查本地是否有token&#34;&gt;1.前端打开页面，检查本地是否有Token
&lt;/h3&gt;&lt;p&gt;用户在浏览器输入 &lt;code&gt;https://www.example.com&lt;/code&gt;，前端代码首先检查&lt;strong&gt;本地存储&lt;/strong&gt;中是否存在有效的Token。&lt;/p&gt;
&lt;h3 id=&#34;2没有token前端跳转登录页用户输入账号密码&#34;&gt;2.没有Token，前端跳转登录页，用户输入账号密码
&lt;/h3&gt;&lt;p&gt;如果本地没有Token，前端路由跳转到 &lt;code&gt;/login&lt;/code&gt; 登录页，用户输入账号和密码，点击【登录】按钮。&lt;/p&gt;
&lt;h3 id=&#34;3前端发送登录请求到api网关&#34;&gt;3.前端发送登录请求到API网关
&lt;/h3&gt;&lt;p&gt;前端将账号密码封装成POST请求，发送到API网关。&lt;/p&gt;
&lt;p&gt;需要注意的是：前端不直接请求认证中心，所有请求都走网关统一入口。&lt;/p&gt;
&lt;h3 id=&#34;4api网关转发登录请求到认证中心&#34;&gt;4.API网关转发登录请求到认证中心
&lt;/h3&gt;&lt;p&gt;API网关收到 &lt;code&gt;/auth/login&lt;/code&gt; 请求，根据路由规则，将请求转发到认证中心服务。&lt;/p&gt;
&lt;h3 id=&#34;5认证中心验证用户信息生成jwt-token&#34;&gt;5.认证中心验证用户信息，生成JWT Token
&lt;/h3&gt;&lt;p&gt;认证中心做以下3件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从数据库查询用户信息，验证账号密码是否正确；&lt;/li&gt;
&lt;li&gt;验证通过后，生成 JWT Token（分为 Access Token 和 Refresh Token）；&lt;/li&gt;
&lt;li&gt;将 Token 返回给前端。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那什么是JWT呢？&lt;/p&gt;
&lt;p&gt;JWT（JSON Web Token）是一种无状态的Token，由三部分组成，用 &lt;code&gt;.&lt;/code&gt; 分隔：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Header&lt;/strong&gt;：头部，存算法和类型；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload&lt;/strong&gt;：载荷，存用户信息（比如userId、username、过期时间）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signature&lt;/strong&gt;：签名，用密钥对Header和Payload签名，防止Token被篡改。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;6前端收到token存储到本地&#34;&gt;6.前端收到Token，存储到本地
&lt;/h3&gt;&lt;p&gt;前端收到认证中心返回的 &lt;code&gt;access_token&lt;/code&gt; 和 &lt;code&gt;refresh_token&lt;/code&gt;，存到本地存储中。&lt;/p&gt;
&lt;h3 id=&#34;7前端携带token请求业务接口&#34;&gt;7.前端携带Token请求业务接口
&lt;/h3&gt;&lt;p&gt;用户登录成功后，前端请求商品列表接口，在请求头 &lt;code&gt;Authorization&lt;/code&gt; 中携带 &lt;code&gt;Bearer Token&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Bearer 这个单词的意思是“持票人”、“持有者”。&lt;/p&gt;
&lt;p&gt;Bearer Token 就是：“谁持有这个 Token，谁就是合法的访问者，服务器就认谁”。&lt;/p&gt;
&lt;h3 id=&#34;8api网关校验token&#34;&gt;8.API网关校验Token
&lt;/h3&gt;&lt;p&gt;API网关收到 &lt;code&gt;/product/list&lt;/code&gt; 请求，通过全局过滤器校验Token的有效性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从请求头 &lt;code&gt;Authorization&lt;/code&gt; 中取出Token；&lt;/li&gt;
&lt;li&gt;验证Token的签名是否正确；&lt;/li&gt;
&lt;li&gt;验证Token是否过期；&lt;/li&gt;
&lt;li&gt;校验通过后，将用户信息（比如userId）放入请求头，转发给微服务；&lt;/li&gt;
&lt;li&gt;如果校验失败，直接返回401未授权。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;9微服务获取用户信息处理业务逻辑&#34;&gt;9.微服务获取用户信息，处理业务逻辑
&lt;/h3&gt;&lt;p&gt;微服务（比如商品服务）收到网关转发的请求，从请求头 &lt;code&gt;X-User-Id&lt;/code&gt; 中取出userId，处理业务逻辑（比如查询该用户的商品列表）。&lt;/p&gt;
&lt;h3 id=&#34;10token过期前端用refresh-token刷新&#34;&gt;10.Token过期，前端用Refresh Token刷新
&lt;/h3&gt;&lt;p&gt;Access Token有效期比较短（比如只有2小时），过期后前端请求会收到401；&lt;/p&gt;
&lt;p&gt;此时前端不用让用户重新登录，而是用&lt;strong&gt;Refresh Token&lt;/strong&gt;去认证中心刷新Access Token。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】Redis的8种内存淘汰策略</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3redis%E7%9A%848%E7%A7%8D%E5%86%85%E5%AD%98%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/</link>
        <pubDate>Mon, 16 Mar 2026 22:54:44 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3redis%E7%9A%848%E7%A7%8D%E5%86%85%E5%AD%98%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/</guid>
        <description>&lt;p&gt;Redis 内存淘汰策略共 8 种，分两大类：不淘汰和淘汰数据；&lt;/p&gt;
&lt;p&gt;纯缓存场景优先选 allkeys-lru，混合场景选 volatile-lru，数据不能丢的话选 noeviction。&lt;/p&gt;
&lt;h2 id=&#34;maxmemory-参数&#34;&gt;&lt;code&gt;maxmemory&lt;/code&gt; 参数
&lt;/h2&gt;&lt;p&gt;只有设置了 &lt;code&gt;maxmemory&lt;/code&gt;（Redis 最大内存限制），内存淘汰策略才会生效。&lt;/p&gt;
&lt;h3 id=&#34;设置-maxmemory的两种方式&#34;&gt;设置 &lt;code&gt;maxmemory&lt;/code&gt;的两种方式
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;配置文件 &lt;code&gt;redis.conf&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-nginx&#34; data-lang=&#34;nginx&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 限制 Redis 最大使用 1GB 内存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;maxmemory&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;1gb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;命令行动态设置（重启后会失效）：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:6379&amp;gt; CONFIG SET maxmemory 1gb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;redis-的8种内存淘汰策略&#34;&gt;Redis 的8种内存淘汰策略
&lt;/h2&gt;&lt;p&gt;可以分为&lt;strong&gt;两大类&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;大类&lt;/th&gt;
          &lt;th&gt;策略名称&lt;/th&gt;
          &lt;th&gt;含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;不淘汰数据&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;noeviction&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;内存满了，新写入直接报错，不淘汰任何数据&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;淘汰过期键&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;volatile-lru&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;淘汰设置了过期时间的键中，&lt;strong&gt;最近最少使用&lt;/strong&gt;的&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;volatile-lfu&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;淘汰设置了过期时间的键中，&lt;strong&gt;最不经常使用&lt;/strong&gt;的&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;volatile-ttl&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;淘汰设置了过期时间的键中，&lt;strong&gt;即将过期&lt;/strong&gt;的&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;volatile-random&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;随机淘汰设置了过期时间的键&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;淘汰所有键&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;allkeys-lru&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;淘汰&lt;strong&gt;所有键&lt;/strong&gt;中，&lt;strong&gt;最近最少使用&lt;/strong&gt;的&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;allkeys-lfu&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;淘汰&lt;strong&gt;所有键&lt;/strong&gt;中，&lt;strong&gt;最不经常使用&lt;/strong&gt;的&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;allkeys-random&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;随机淘汰&lt;strong&gt;所有键&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;1-noeviction&#34;&gt;1. &lt;code&gt;noeviction&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;当内存满了，新写入的请求（比如 &lt;code&gt;SET&lt;/code&gt;、&lt;code&gt;LPUSH&lt;/code&gt;）会直接报错 &lt;code&gt;(error) OOM command not allowed when used memory &amp;gt; &#39;maxmemory&#39;&lt;/code&gt;，&lt;strong&gt;不淘汰任何数据&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;适用于&lt;/strong&gt;数据绝对不能丢，且有完善的内存监控和扩容机制（比如金融级核心数据）的场景。&lt;/p&gt;
&lt;h3 id=&#34;2-淘汰过期键的4种策略&#34;&gt;2. 淘汰过期键的4种策略
&lt;/h3&gt;&lt;p&gt;这类策略只淘汰设置了 &lt;code&gt;expire&lt;/code&gt; 过期时间的键，不会碰永久键，适合“缓存+持久化”混合的场景（比如永久键存重要数据，过期键存缓存）。&lt;/p&gt;
&lt;h4 id=&#34;volatile-lru&#34;&gt;&lt;code&gt;volatile-lru&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;LRU = Least Recently Used（最近最少使用）。&lt;/p&gt;
&lt;p&gt;在设置了过期时间的键中，淘汰&lt;strong&gt;最久没被访问过&lt;/strong&gt;的键。&lt;/p&gt;
&lt;p&gt;比如缓存场景，往往会希望保留“最近刚用过的热数据”。&lt;/p&gt;
&lt;h4 id=&#34;volatile-lfu&#34;&gt;&lt;code&gt;volatile-lfu&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;LFU = Least Frequently Used（最不经常使用）。&lt;/p&gt;
&lt;p&gt;在设置了过期时间的键中，淘汰&lt;strong&gt;访问次数最少&lt;/strong&gt;的键。&lt;/p&gt;
&lt;h4 id=&#34;volatile-ttl&#34;&gt;&lt;code&gt;volatile-ttl&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;TTL = Time To Live（剩余生存时间）。&lt;/p&gt;
&lt;p&gt;在设置了过期时间的键中，淘汰&lt;strong&gt;剩余时间最短、即将过期&lt;/strong&gt;的键。&lt;/p&gt;
&lt;h4 id=&#34;volatile-random&#34;&gt;&lt;code&gt;volatile-random&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;在设置了过期时间的键中，&lt;strong&gt;随机淘汰&lt;/strong&gt;一个。&lt;/p&gt;
&lt;h3 id=&#34;3-淘汰所有键的3种策略&#34;&gt;3. 淘汰所有键的3种策略
&lt;/h3&gt;&lt;p&gt;这类策略&lt;strong&gt;不管键有没有设置过期时间，都会淘汰&lt;/strong&gt;，适合纯缓存场景（所有数据都是缓存，丢了也没关系，可以从数据库重新加载）。&lt;/p&gt;
&lt;h4 id=&#34;allkeys-lru&#34;&gt;&lt;code&gt;allkeys-lru&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;在所有键中，淘汰&lt;strong&gt;最近最少使用&lt;/strong&gt;的键。&lt;/p&gt;
&lt;p&gt;比较适合电商商品缓存、用户Session缓存这些纯缓存场景。&lt;/p&gt;
&lt;h4 id=&#34;allkeys-lfu&#34;&gt;&lt;code&gt;allkeys-lfu&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;在所有键中，淘汰&lt;strong&gt;访问次数最少&lt;/strong&gt;的键。&lt;/p&gt;
&lt;h4 id=&#34;allkeys-random&#34;&gt;&lt;code&gt;allkeys-random&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;在所有键中，&lt;strong&gt;随机淘汰&lt;/strong&gt;一个。&lt;/p&gt;
&lt;h2 id=&#34;lru-vs-lfu到底有啥区别&#34;&gt;LRU vs LFU，到底有啥区别？
&lt;/h2&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;对比维度&lt;/th&gt;
          &lt;th&gt;LRU（最近最少使用）&lt;/th&gt;
          &lt;th&gt;LFU（最不经常使用）&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;核心依据&lt;/td&gt;
          &lt;td&gt;看最近多久没被访问&lt;/td&gt;
          &lt;td&gt;看访问次数多少&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;优点&lt;/td&gt;
          &lt;td&gt;实现简单，符合“热数据最近常用”的直觉&lt;/td&gt;
          &lt;td&gt;更精准，能识别“真正的热数据”（比如某个键虽然最近没被访问，但总访问次数极高）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;缺点&lt;/td&gt;
          &lt;td&gt;可能误删“偶尔访问一次的冷数据，但其实总访问次数很高”&lt;/td&gt;
          &lt;td&gt;实现复杂，需要额外空间记录访问次数&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;举个例子&#34;&gt;举个例子
&lt;/h3&gt;&lt;p&gt;假设键A昨天被访问100次，今天没被访问；&lt;/p&gt;
&lt;p&gt;键B今天被访问1次。&lt;/p&gt;
&lt;p&gt;因为最近没被访问，所以LRU会删键A。&lt;/p&gt;
&lt;p&gt;但是，因为总访问次数只有1次，所以LFU会删键B。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>MySQL的三层B&#43;树能存多少数据？</title>
        <link>https://iamxurulin.github.io/p/mysql%E7%9A%84%E4%B8%89%E5%B1%82b-%E6%A0%91%E8%83%BD%E5%AD%98%E5%A4%9A%E5%B0%91%E6%95%B0%E6%8D%AE/</link>
        <pubDate>Mon, 16 Mar 2026 13:09:19 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/mysql%E7%9A%84%E4%B8%89%E5%B1%82b-%E6%A0%91%E8%83%BD%E5%AD%98%E5%A4%9A%E5%B0%91%E6%95%B0%E6%8D%AE/</guid>
        <description>&lt;p&gt;InnoDB读写磁盘的最小单位是&lt;strong&gt;页（Page）&lt;/strong&gt;，默认大小&lt;strong&gt;16KB&lt;/strong&gt;。&lt;br&gt;
而MySQL的主键索引B+树，每一个树节点（不管是根节点、中间节点、叶子节点），&lt;strong&gt;本质上就是一个16KB的页&lt;/strong&gt;。&lt;br&gt;
所有计算，都是围绕【1个16KB的页，能存多少东西】展开的。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;节点类型&lt;/th&gt;
          &lt;th&gt;存的内容&lt;/th&gt;
          &lt;th&gt;特点&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;非叶子节点（根节点、中间层节点）&lt;/td&gt;
          &lt;td&gt;只存主键值 + 指向下一层页的指针&lt;/td&gt;
          &lt;td&gt;不存完整用户数据，体积极小，一个页能存上千个&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;叶子节点（最底层节点）&lt;/td&gt;
          &lt;td&gt;存用户的完整一行行数据&lt;/td&gt;
          &lt;td&gt;存真实业务数据，体积大，一个页能存的行数有限&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;1先算1个非叶子节点能存多少个索引项&#34;&gt;1.先算1个非叶子节点，能存多少个索引项
&lt;/h3&gt;&lt;p&gt;非叶子节点存的是「主键值 + 指针」，我们先算单个索引项的大小：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;主键大小&lt;/strong&gt;：假设主键是&lt;code&gt;bigint&lt;/code&gt;类型，MySQL里&lt;code&gt;bigint&lt;/code&gt;固定占&lt;strong&gt;8字节&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;指针大小&lt;/strong&gt;：InnoDB里指向页的指针，固定占&lt;strong&gt;6字节&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单个索引项总大小&lt;/strong&gt;：8字节（主键） + 6字节（指针） = &lt;strong&gt;14字节&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一个页是16KB，换算成字节是 &lt;code&gt;16 × 1024 = 16384字节&lt;/code&gt;。&lt;br&gt;
所以一个非叶子页能存的索引项数量：&lt;br&gt;
&lt;code&gt;16384 ÷ 14 ≈ 1170个&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;2再算1个叶子节点能存多少行数据&#34;&gt;2.再算1个叶子节点，能存多少行数据
&lt;/h3&gt;&lt;p&gt;叶子节点存的是用户的完整一行行数据，&lt;strong&gt;假设&lt;/strong&gt;一行业务数据占1KB（比如一张表有id、name、age、create_time等字段，加起来差不多1KB）。&lt;/p&gt;
&lt;p&gt;一个页是16KB，所以一个叶子页能存的行数：&lt;br&gt;
&lt;code&gt;16KB ÷ 1KB/行 = 16行&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;3最后算三层b树的总数据量&#34;&gt;3.最后算三层B+树的总数据量
&lt;/h3&gt;&lt;p&gt;顺着三层结构，从根节点往下算：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一层（根节点）&lt;/strong&gt;：1个非叶子页，能存1170个索引项 → 能指向1170个第二层的页；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二层（中间非叶子节点）&lt;/strong&gt;：1170个非叶子页，每个页又能存1170个索引项 → 能指向 &lt;code&gt;1170 × 1170 = 1368900&lt;/code&gt; 个第三层的叶子页；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三层（叶子节点）&lt;/strong&gt;：每个叶子页能存16行数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终总数据量 = 叶子页总数 × 每个叶子页的行数：&lt;br&gt;
&lt;code&gt;1170 × 1170 × 16 ≈ 2190万条&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以，三层B+树大约能存2000万条记录。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题拆解】Java类加载过程&#43;双亲委派机制</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B-%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Sun, 15 Mar 2026 22:44:04 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E6%8B%86%E8%A7%A3java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B-%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;h2 id=&#34;什么是类加载&#34;&gt;什么是类加载？
&lt;/h2&gt;&lt;p&gt;简单来说，类加载就是把&lt;code&gt;.class&lt;/code&gt;文件（编译后的字节码）从磁盘/网络加载到JVM内存里，并生成对应的&lt;code&gt;Class&lt;/code&gt;对象的过程。&lt;/p&gt;
&lt;h2 id=&#34;java类加载的5个核心阶段&#34;&gt;Java类加载的5个核心阶段
&lt;/h2&gt;&lt;p&gt;类加载不是一步到位的，而是分成了5个阶段：
&lt;strong&gt;加载➡️验证➡️准备➡️解析➡️初始化&lt;/strong&gt;，
其中&lt;strong&gt;加载、验证、准备、初始化&lt;/strong&gt;的顺序是固定的，&lt;strong&gt;解析&lt;/strong&gt;可能在初始化之后（为了支持动态绑定）。&lt;/p&gt;
&lt;h3 id=&#34;1-加载loading&#34;&gt;1. 加载（Loading）
&lt;/h3&gt;&lt;p&gt;这是类加载的第一步，举个例子：假如你要加载User类，JVM会先找到User.class文件，把它的信息存到方法区，然后在堆里生成一个Class&lt;User&gt;对象，以后你要访问User类的信息，都通过这个Class对象。&lt;/p&gt;
&lt;h3 id=&#34;2-验证verification&#34;&gt;2. 验证（Verification）
&lt;/h3&gt;&lt;p&gt;这一步是为了保证JVM的安全，防止恶意或不规范的&lt;code&gt;.class&lt;/code&gt;文件搞破坏，主要验证四个方面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;文件格式验证&lt;/strong&gt;：检查&lt;code&gt;.class&lt;/code&gt;文件的魔数（开头的&lt;code&gt;CAFEBABE&lt;/code&gt;）、版本号（JDK版本兼容）等；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元数据验证&lt;/strong&gt;：检查类的继承关系（比如不能继承&lt;code&gt;final&lt;/code&gt;类）、方法重写是否合法等；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字节码验证&lt;/strong&gt;：检查字节码指令是否安全（比如不会跳转到非法指令、不会访问越界的数组）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;符号引用验证&lt;/strong&gt;：检查符号引用（比如类名、方法名）是否能找到对应的类/方法。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-准备preparation&#34;&gt;3. 准备（Preparation）
&lt;/h3&gt;&lt;p&gt;这一步是在&lt;strong&gt;方法区&lt;/strong&gt;里为类的&lt;strong&gt;静态变量&lt;/strong&gt;（&lt;code&gt;static&lt;/code&gt;修饰的变量）分配内存，并设置&lt;strong&gt;初始默认值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;需要注意的是：不是代码里写的初始值，而是以下这些初始值：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;数据类型&lt;/th&gt;
          &lt;th&gt;默认值&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;int&lt;/td&gt;
          &lt;td&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;long&lt;/td&gt;
          &lt;td&gt;0L&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;float&lt;/td&gt;
          &lt;td&gt;0.0f&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;double&lt;/td&gt;
          &lt;td&gt;0.0d&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;boolean&lt;/td&gt;
          &lt;td&gt;false&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;引用类型&lt;/td&gt;
          &lt;td&gt;null&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;4-解析resolution&#34;&gt;4. 解析（Resolution）
&lt;/h3&gt;&lt;p&gt;这一步是把常量池里的&lt;strong&gt;符号引用&lt;/strong&gt;（比如类名&lt;code&gt;com.example.User&lt;/code&gt;、方法名&lt;code&gt;getUser&lt;/code&gt;），转换成&lt;strong&gt;直接引用&lt;/strong&gt;（比如内存地址、指针）。&lt;/p&gt;
&lt;p&gt;解析的话，主要针对的是类、接口、字段、方法等符号引用。&lt;/p&gt;
&lt;p&gt;举个通俗一点的例子：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;符号引用&lt;/strong&gt;就是一个“代号”，比如你用“张三”指代一个人，不管他在哪，“张三”这个代号都能用；&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;直接引用&lt;/strong&gt;就是“具体的地址”，比如张三现在在“XX小区XX号楼XX室”，你可以直接找到他。&lt;/p&gt;
&lt;h3 id=&#34;5-初始化initialization&#34;&gt;5. 初始化（Initialization）
&lt;/h3&gt;&lt;p&gt;这是类加载的最后一步，也是真正执行程序员写的代码的阶段。&lt;/p&gt;
&lt;p&gt;这一步只做一件事：&lt;strong&gt;执行类构造器&lt;/strong&gt;&lt;code&gt;&amp;lt;clinit&amp;gt;()&lt;/code&gt;&lt;strong&gt;方法&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;clinit&amp;gt;()&lt;/code&gt;方法是编译器自动生成的，它会把类里的&lt;strong&gt;静态变量赋值语句&lt;/strong&gt;和&lt;strong&gt;静态代码块&lt;/strong&gt;（&lt;code&gt;static {}&lt;/code&gt;）按顺序合并起来执行。&lt;/p&gt;
&lt;h4 id=&#34;举个例子&#34;&gt;举个例子
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;num&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 静态变量赋值&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 静态代码块&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;num&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;静态代码块执行&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以上代码在初始化阶段，JVM会执行&lt;code&gt;&amp;lt;clinit&amp;gt;()&lt;/code&gt;方法：&lt;/p&gt;
&lt;p&gt;先把num设为10，再设为20，然后打印“静态代码块执行”。&lt;/p&gt;
&lt;h4 id=&#34;什么时候会触发类的初始化&#34;&gt;什么时候会触发类的初始化？
&lt;/h4&gt;&lt;p&gt;有以下6种情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建类的实例（&lt;code&gt;new User()&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;访问类的静态变量或静态方法（&lt;code&gt;User.num&lt;/code&gt;、&lt;code&gt;User.getUser()&lt;/code&gt;）；&lt;/li&gt;
&lt;li&gt;用&lt;code&gt;Class.forName(&amp;quot;com.example.User&amp;quot;)&lt;/code&gt;&lt;strong&gt;反射&lt;/strong&gt;加载类；&lt;/li&gt;
&lt;li&gt;初始化子类时，会先初始化父类；&lt;/li&gt;
&lt;li&gt;JVM启动时，会先初始化包含&lt;code&gt;main()&lt;/code&gt;方法的主类；&lt;/li&gt;
&lt;li&gt;JDK 7+的动态语言支持，&lt;code&gt;MethodHandle&lt;/code&gt;解析结果为&lt;code&gt;REF_getStatic&lt;/code&gt;/&lt;code&gt;REF_putStatic&lt;/code&gt;/&lt;code&gt;REF_invokeStatic&lt;/code&gt;的句柄，且对应的类未初始化。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;双亲委派机制parent-delegation-model&#34;&gt;双亲委派机制（Parent Delegation Model）
&lt;/h2&gt;&lt;h3 id=&#34;什么是类加载器&#34;&gt;什么是类加载器？
&lt;/h3&gt;&lt;p&gt;类加载器（ClassLoader）就是负责&lt;strong&gt;加载类的工具&lt;/strong&gt;，JVM内置了三种类加载器：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;启动类加载器（Bootstrap ClassLoader）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用C++写的，不是&lt;code&gt;ClassLoader&lt;/code&gt;的子类，开发者无法直接获取它的引用。&lt;/p&gt;
&lt;p&gt;负责加载JDK核心类库（比如&lt;code&gt;java.lang.Object&lt;/code&gt;、&lt;code&gt;java.lang.String&lt;/code&gt;），这些类在&lt;code&gt;jre/lib/rt.jar&lt;/code&gt;里。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;扩展类加载器（Extension ClassLoader）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;是&lt;code&gt;ClassLoader&lt;/code&gt;的子类，开发者可以获取它的引用，负责加载JDK扩展类库（比如jre/lib/ext目录下的类）。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;应用程序类加载器（Application ClassLoader）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;是&lt;code&gt;ClassLoader&lt;/code&gt;的子类，也是默认的类加载器。负责加载用户自己写的类（比如&lt;code&gt;com.example.User&lt;/code&gt;），也就是&lt;code&gt;classpath&lt;/code&gt;下的类。&lt;/p&gt;
&lt;h3 id=&#34;什么是双亲委派机制&#34;&gt;什么是双亲委派机制？
&lt;/h3&gt;&lt;p&gt;这里的“双亲”不是指“父母”，而是指“父类加载器”，是翻译的问题，理解成“父委派机制”更准确一点。&lt;/p&gt;
&lt;p&gt;双亲委派机制的核心规则是：&lt;strong&gt;“向上委托，向下加载”&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当一个类加载器收到加载类的请求时，它&lt;strong&gt;不会自己先加载&lt;/strong&gt;，而是把请求&lt;strong&gt;委托给父类加载器&lt;/strong&gt;去加载；&lt;/li&gt;
&lt;li&gt;如果父类加载器能加载这个类，就由父类加载器加载；&lt;/li&gt;
&lt;li&gt;如果父类加载器加载不了（比如在它的加载范围内找不到这个类），才会&lt;strong&gt;向下交给子类加载器&lt;/strong&gt;自己加载。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;举个例子-1&#34;&gt;举个例子
&lt;/h4&gt;&lt;p&gt;比如说：加载&lt;code&gt;java.lang.Object&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;应用程序类加载器收到加载&lt;code&gt;Object&lt;/code&gt;的请求，它不自己加载，委托给扩展类加载器；&lt;/li&gt;
&lt;li&gt;扩展类加载器也不自己加载，委托给启动类加载器；&lt;/li&gt;
&lt;li&gt;启动类加载器在&lt;code&gt;rt.jar&lt;/code&gt;里找到了&lt;code&gt;Object&lt;/code&gt;类，直接加载它；&lt;/li&gt;
&lt;li&gt;加载完成，返回&lt;code&gt;Class&lt;/code&gt;对象，不会再向下交给子类加载器。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;为什么需要双亲委派机制&#34;&gt;为什么需要双亲委派机制？
&lt;/h3&gt;&lt;p&gt;双亲委派机制的目的是&lt;strong&gt;保证Java程序的安全性和稳定性&lt;/strong&gt;，主要解决了两个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;避免类的重复加载&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果没有双亲委派，用户自己写一个&lt;code&gt;java.lang.Object&lt;/code&gt;类，应用程序类加载器就会加载它，导致JVM里有两个&lt;code&gt;Object&lt;/code&gt;类，程序就乱了。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;保证核心类库的安全&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;防止用户恶意篡改核心类库（比如写一个恶意的&lt;code&gt;java.lang.String&lt;/code&gt;类），因为核心类库只会由启动类加载器加载，用户写的同名类不会被加载。&lt;/p&gt;
&lt;h3 id=&#34;破坏双亲委派机制的情况&#34;&gt;破坏双亲委派机制的情况
&lt;/h3&gt;&lt;p&gt;常见的破坏双亲委派机制的场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JDBC的DriverManager&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JDBC需要加载不同数据库的驱动（比如MySQL驱动），但驱动类是用户写的，启动类加载器加载不了，所以&lt;code&gt;DriverManager&lt;/code&gt;用了&lt;strong&gt;线程上下文类加载器&lt;/strong&gt;（Thread Context ClassLoader），打破了双亲委派，让启动类加载器能委托应用程序类加载器加载驱动。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;Tomcat的类加载器&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tomcat需要隔离不同Web应用的类（比如两个应用都用了不同版本的Spring），所以它有自己的类加载器结构，每个Web应用有独立的类加载器，优先加载自己的类，而不是向上委托。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题】能讲讲MySQL的锁机制吗</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E8%83%BD%E8%AE%B2%E8%AE%B2mysql%E7%9A%84%E9%94%81%E6%9C%BA%E5%88%B6%E5%90%97/</link>
        <pubDate>Sun, 15 Mar 2026 16:44:46 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E8%83%BD%E8%AE%B2%E8%AE%B2mysql%E7%9A%84%E9%94%81%E6%9C%BA%E5%88%B6%E5%90%97/</guid>
        <description>&lt;p&gt;MySQL的锁按两个维度分，刚好对应开发中最常遇到的问题：&lt;/p&gt;
&lt;p&gt;按&lt;strong&gt;解决并发冲突&lt;/strong&gt;的思路分，是悲观锁和乐观锁——这俩决定了“你是先占坑再干活，还是先干活最后再检查”；&lt;/p&gt;
&lt;p&gt;按&lt;strong&gt;锁的范围大小&lt;/strong&gt;分，是表锁、行锁，还有 InnoDB 特有的间隙锁 + 临键锁 —— 这三类锁决定了 “你是锁整个仓库（表锁）、只锁你要的那一件商品（行锁），还是锁仓库里的某个货架区间（间隙锁 + 临键锁）”。&lt;/p&gt;
&lt;p&gt;先建一张最简单的电商商品表 &lt;code&gt;product&lt;/code&gt;，插几条测试数据，等会更好理解举的例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;CREATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;TABLE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COMMENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;主键ID&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;varchar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COMMENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;商品名称&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COMMENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;库存&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NOT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COMMENT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;乐观锁版本号&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;PRIMARY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;KEY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ENGINE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InnoDB&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;DEFAULT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CHARSET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;utf8mb4&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;COMMENT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;商品表&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 插入测试数据：id=1是iPhone，库存100；id=3是华为，库存200；id=5是小米，库存150
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;iPhone 17&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Mate 70&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Xiaomi 15&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;150&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;悲观锁&#34;&gt;悲观锁
&lt;/h2&gt;&lt;p&gt;举个&lt;strong&gt;电商秒杀扣库存&lt;/strong&gt;的真实例子，也是悲观锁最典型的应用：&lt;/p&gt;
&lt;p&gt;比如有两个用户同时秒杀iPhone 17，库存100。&lt;/p&gt;
&lt;p&gt;如果不用锁，俩人同时查出来库存100，都扣减1，就超卖了。&lt;br&gt;
用悲观锁的话，就是在查库存的时候用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; 显式加排他锁——&lt;/p&gt;
&lt;p&gt;第一个用户的事务先把这行数据锁住，第二个用户的查询就会阻塞，等第一个用户扣减完库存、提交事务释放锁，第二个用户才能拿到最新的库存，这样就保证了不超卖。&lt;/p&gt;
&lt;h2 id=&#34;乐观锁&#34;&gt;乐观锁
&lt;/h2&gt;&lt;p&gt;这也是开发中很常见的场景：&lt;strong&gt;两个运营同时修改同一件商品的标题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这种场景并发冲突概率很低（因为很少会有两个人同时改同一个商品的标题），如果用悲观锁，每次修改都要加锁，太影响性能了，所以用&lt;strong&gt;乐观锁&lt;/strong&gt;更合适。&lt;/p&gt;
&lt;p&gt;乐观锁的意思就是：我&lt;strong&gt;假设没人跟我抢&lt;/strong&gt;，所以我先直接修改，最后提交的时候再检查一下：&lt;/p&gt;
&lt;p&gt;在我修改的这段时间里，有没有别人也改过这条数据？&lt;/p&gt;
&lt;p&gt;如果没有，就修改成功；&lt;/p&gt;
&lt;p&gt;如果有，就提示用户“数据已被修改，请刷新后重试”。&lt;/p&gt;
&lt;h3 id=&#34;举个具体的sql操作实例&#34;&gt;举个具体的SQL操作实例
&lt;/h3&gt;&lt;p&gt;我们打开两个命令行窗口，分表代表两个会话。&lt;/p&gt;
&lt;h4 id=&#34;1会话a运营a先查商品&#34;&gt;1.会话A（运营A先查商品）
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 1. 运营A查出来iPhone的信息，此时 version = 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;2会话b运营b同时查商品&#34;&gt;2.会话B（运营B同时查商品）
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 1. 运营B也查出来iPhone的信息，此时 version 也是 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;3会话a运营a先提交修改&#34;&gt;3.会话A（运营A先提交修改）
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 1. 运营A把标题改成“iPhone 17 Pro Max”，WHERE条件带上 version = 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;iPhone 17 Pro Max&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 现象：返回更新行数为 1，修改成功！此时数据库里的 version 变成了 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;4会话b运营b后提交修改&#34;&gt;4.会话B（运营B后提交修改）
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 1. 运营B把标题改成“iPhone 17 特价版”，WHERE条件也带上 version = 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;iPhone 17 特价版&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AND&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 现象：返回更新行数为 0，修改失败！
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 因为数据库里的 version 已经变成 1 了，找不到 id=1 AND version=0 的行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 此时业务代码就可以提示用户：“商品已被其他运营修改，请刷新页面后重试”
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;表锁&#34;&gt;表锁
&lt;/h2&gt;&lt;p&gt;直接锁&lt;strong&gt;整张表&lt;/strong&gt;，其他会话对这张表的任何读写（不管是查哪一行、改哪一行）都会被阻塞。&lt;/p&gt;
&lt;h4 id=&#34;举个例子&#34;&gt;举个例子
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：给全表商品库存+10，显式加表锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;LOCK&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TABLES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WRITE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 此时会话B想查id=1的商品，会直接阻塞！
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A提交事务并释放表锁后，会话B才能执行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;UNLOCK&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TABLES&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;表锁的优点是加锁开销小、速度快，不会死锁，但缺点就是并发度极低，一锁全表，电商场景下用它会直接让网站卡死。&lt;/p&gt;
&lt;h2 id=&#34;行锁&#34;&gt;行锁
&lt;/h2&gt;&lt;p&gt;只锁&lt;strong&gt;具体的某一行数据&lt;/strong&gt;，其他行完全不受影响，是InnoDB支持高并发的核心。&lt;/p&gt;
&lt;p&gt;需要注意的是行锁的&lt;strong&gt;前提&lt;/strong&gt;是【走索引】。&lt;/p&gt;
&lt;p&gt;如果你的查询没走索引，InnoDB会&lt;strong&gt;退化成表锁&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id=&#34;举两个例子对比一下&#34;&gt;举两个例子对比一下
&lt;/h4&gt;&lt;p&gt;先看&lt;strong&gt;走索引的情况（行锁生效）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：用主键id=1查，加排他锁（走主键索引，只锁id=1这行）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：查id=3的商品，完全不阻塞！直接拿到锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 执行成功
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：查id=1的商品，会阻塞！需要等会话A释放锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再看&lt;strong&gt;没走索引的情况（退化成表锁）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 先确认name字段没加索引（前面建表的时候只给id加了主键）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：用name=&amp;#39;iPhone 17&amp;#39;查，加排他锁（没走索引，锁全表！）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;iPhone 17&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：查id=3的商品，居然也阻塞了！因为锁了全表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;行锁的优点是并发度极高，只锁需要的行，不影响其他操作；但缺点是加锁开销大、速度慢，&lt;strong&gt;可能产生死锁&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;间隙锁临键锁&#34;&gt;间隙锁+临键锁
&lt;/h2&gt;&lt;p&gt;这俩是InnoDB特有的，专门用来解决【幻读】问题的。&lt;/p&gt;
&lt;h4 id=&#34;先举个电商场景的例子说明一下什么是幻读&#34;&gt;先举个电商场景的例子说明一下什么是幻读？
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：查id&amp;gt;1的所有商品（此时表里只有id=1、3、5）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 第一次查：查到id=3、5两条
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：插了一条id=2的商品，提交事务
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;OPPO Find X8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;COMMIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：再查id&amp;gt;1的商品，居然查到了id=2、3、5三条！
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 第二次查：多了一条，像产生了幻觉
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这就是&lt;strong&gt;幻读&lt;/strong&gt;：同一个事务里，两次相同的范围查询，结果却是不一样的。&lt;/p&gt;
&lt;h4 id=&#34;间隙锁gap-lock&#34;&gt;间隙锁（Gap Lock）
&lt;/h4&gt;&lt;p&gt;间隙锁，间隙锁，顾名思义，它不锁具体的行，锁的是两个索引值之间的“间隙”，也就是只锁&lt;strong&gt;两个相邻索引值之间的空白区间&lt;/strong&gt;，以防止别人在这个区间里插数据。&lt;/p&gt;
&lt;p&gt;以刚开始建的那个product表为例：&lt;/p&gt;
&lt;p&gt;id=1、3、5，间隙就是：&lt;code&gt;(-∞,1)&lt;/code&gt;、&lt;code&gt;(1,3)&lt;/code&gt;、&lt;code&gt;(3,5)&lt;/code&gt;、&lt;code&gt;(5,+∞)&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;临键锁next-key-lock&#34;&gt;临键锁（Next-Key Lock）
&lt;/h4&gt;&lt;p&gt;临键锁其实是【行锁】+【间隙锁】的组合，锁的是&lt;strong&gt;左开右闭&lt;/strong&gt;的区间。&lt;/p&gt;
&lt;p&gt;比如&lt;code&gt;(1,3]&lt;/code&gt;（锁id=3这一行，同时锁1到3之间的间隙）。&lt;/p&gt;
&lt;p&gt;还是以刚开始建的那个product表为例，临键区间就是：&lt;code&gt;(-∞,1]&lt;/code&gt;、&lt;code&gt;(1,3]&lt;/code&gt;、&lt;code&gt;(3,5]&lt;/code&gt;、&lt;code&gt;(5,+∞)&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;举个例子-1&#34;&gt;举个例子
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：用id=3查，加排他锁（走主键索引，加临键锁(1,3]）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：想插id=2的商品，直接阻塞！因为间隙(1,3)被锁了
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;OPPO Find X8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：想插id=6的商品，不阻塞！因为(5,+∞)没被锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;INSERT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;INTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;VALUES&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Vivo X200&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 执行成功
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;小贴士&#34;&gt;&lt;strong&gt;小贴士&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;间隙锁 + 临键锁只在 InnoDB 的默认隔离级别【可重复读（RR）】下生效。&lt;/p&gt;
&lt;p&gt;如果是【读提交（RC）】隔离级别，会关闭间隙锁，只保留行锁。&lt;/p&gt;
&lt;h2 id=&#34;夺命连环问什么是死锁又怎么避免呢&#34;&gt;夺命连环问：什么是死锁？又怎么避免呢？
&lt;/h2&gt;&lt;p&gt;两个或多个会话，&lt;strong&gt;互相持有对方需要的锁&lt;/strong&gt;，都在等待对方释放，导致谁都执行不下去，形成“死循环”，这就是死锁。&lt;/p&gt;
&lt;h4 id=&#34;举个例子-2&#34;&gt;举个例子
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话A：先锁id=1，再想锁id=3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 持有id=1的锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 此时会话A想拿id=3的锁，但被会话B持有了，阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 会话B：先锁id=3，再想锁id=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 持有id=3的锁
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 此时会话B想拿id=1的锁，但被会话A持有了，阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FOR&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UPDATE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;-- 阻塞
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 结果：死锁！
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;死锁的四个必要条件&#34;&gt;死锁的四个必要条件
&lt;/h4&gt;&lt;p&gt;只要破坏其中任意一个，就能避免死锁：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;互斥条件&lt;/strong&gt;：一个锁只能被一个会话持有（行锁就是这样，没法破坏）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;请求与保持条件&lt;/strong&gt;：持有一个锁的同时，又请求另一个锁；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不剥夺条件&lt;/strong&gt;：锁只能由持有者主动释放，不能被别人强行抢走；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;循环等待条件&lt;/strong&gt;：会话之间形成循环等待链（A等B，B等A）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;怎么避免死锁&#34;&gt;怎么避免死锁？
&lt;/h4&gt;&lt;h4 id=&#34;-按固定顺序加锁&#34;&gt;① 按固定顺序加锁
&lt;/h4&gt;&lt;p&gt;所有会话都按&lt;strong&gt;相同的顺序&lt;/strong&gt;加锁，比如都先锁id小的，再锁id大的，这样就不会形成循环等待。&lt;/p&gt;
&lt;h4 id=&#34;-减少锁的范围和持有时间&#34;&gt;② 减少锁的范围和持有时间
&lt;/h4&gt;&lt;p&gt;尽量用&lt;strong&gt;行锁&lt;/strong&gt;不用表锁；&lt;/p&gt;
&lt;p&gt;事务尽量&lt;strong&gt;小&lt;/strong&gt;，不要在事务里做无关操作（比如不要在事务里调第三方接口、查无关表），锁持有时间越短，死锁概率越低。&lt;/p&gt;
&lt;h4 id=&#34;-避免大事务&#34;&gt;③ 避免大事务
&lt;/h4&gt;&lt;p&gt;大事务持有锁的时间长，涉及的行多，更容易死锁，把大事务拆成多个小事务。&lt;/p&gt;
&lt;h4 id=&#34;小贴士-1&#34;&gt;&lt;strong&gt;小贴士&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;InnoDB 有自动死锁检测机制，它会实时扫描锁等待链，一旦发现死锁，会自动回滚其中一个，一般是代价较小的那个事务，比如更新行数少的那个，让另一个事务继续执行，不会让系统无限期卡死。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>TCP的核心参数-MTU、MSS、RTT、RTO、cwnd、ssthresh</title>
        <link>https://iamxurulin.github.io/p/tcp%E7%9A%84%E6%A0%B8%E5%BF%83%E5%8F%82%E6%95%B0-mtumssrttrtocwndssthresh/</link>
        <pubDate>Sun, 15 Mar 2026 11:36:23 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/tcp%E7%9A%84%E6%A0%B8%E5%BF%83%E5%8F%82%E6%95%B0-mtumssrttrtocwndssthresh/</guid>
        <description>&lt;p&gt;MTU和MSS，管【单个TCP包到底发多大最合适】，是整个传输的基础；&lt;/p&gt;
&lt;p&gt;RTT和RTO，管【包发出去之后，怎么保证对方一定收到，丢包了怎么办】，是TCP可靠传输的核心；&lt;/p&gt;
&lt;p&gt;cwnd和ssthresh，管【一次能连续发多少个包，怎么既跑满带宽，又不把网络堵死】，是TCP拥塞控制的核心。&lt;/p&gt;
&lt;h3 id=&#34;mtu--mss&#34;&gt;MTU &amp;amp; MSS
&lt;/h3&gt;&lt;h4 id=&#34;mtu最大传输单元maximum-transmission-unit&#34;&gt;&lt;strong&gt;MTU（最大传输单元，Maximum Transmission Unit）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;平时用的以太网、WiFi，硬件层面就规定了一个完整的IP数据包，最大不能超过1500字节，这个上限就是MTU。&lt;br&gt;
如果IP包超过MTU，就会被拆成好几个小分片传输，只要有一个分片丢了，整个包就得全量重传，特别影响效率，还可能被防火墙拦截。&lt;/p&gt;
&lt;h4 id=&#34;mss最大报文段长度maximum-segment-size&#34;&gt;&lt;strong&gt;MSS（最大报文段长度，Maximum Segment Size）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;这是基于MTU算出来的、TCP包里真正能装的&lt;strong&gt;业务数据的最大长度&lt;/strong&gt;——要把IP头、TCP头的空间提前扣掉。&lt;br&gt;
以太网默认MTU1500字节，标准IP头和TCP头各占20字节，所以默认MSS就是1460字节。&lt;/p&gt;
&lt;p&gt;它是TCP三次握手的时候，客户端和服务端协商出来的，最终取两边的最小值，目的就是从源头避免IP拆包。&lt;/p&gt;
&lt;h3 id=&#34;rtt--rto&#34;&gt;RTT &amp;amp; RTO
&lt;/h3&gt;&lt;h4 id=&#34;rtt往返时延round-trip-time&#34;&gt;&lt;strong&gt;RTT（往返时延，Round-Trip Time）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;我发一个TCP包出去，到我收到对方回的【我收到了】的ACK确认包，这一来一回花的时间，就是RTT。&lt;br&gt;
它是网络环境决定的客观值，网络越拥堵、跨的路由越多，RTT就越长，会实时波动，是算RTO的唯一基础。&lt;/p&gt;
&lt;h4 id=&#34;rto重传超时时间retransmission-timeout&#34;&gt;&lt;strong&gt;RTO（重传超时时间，Retransmission TimeOut）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;我发出去一个包，会设一个定时器，要是超过这个时间还没收到ACK，我就认为这个包丢了，马上重发一个，这个定时器的时间就是RTO。&lt;br&gt;
RTO完全是基于RTT算出来的，不是固定死的。&lt;/p&gt;
&lt;p&gt;它必须比RTT大一点。&lt;/p&gt;
&lt;p&gt;如果太小了，ACK还在路上就瞎重传，浪费带宽；&lt;/p&gt;
&lt;p&gt;但太大了，真丢包了半天不重传，传输变慢。&lt;/p&gt;
&lt;p&gt;所以，网络波动RTT变了，RTO也会跟着实时调整。&lt;/p&gt;
&lt;h3 id=&#34;cwnd--ssthresh&#34;&gt;cwnd &amp;amp; ssthresh
&lt;/h3&gt;&lt;p&gt;这两个参数是TCP拥塞控制的核心，TCP刚建连的时候，根本不知道当前网络能扛住多大流量，发少了浪费带宽，发多了直接把网络堵死，所以用这两个参数控制发送节奏。&lt;/p&gt;
&lt;h4 id=&#34;cwnd拥塞窗口congestion-window&#34;&gt;&lt;strong&gt;cwnd（拥塞窗口，Congestion Window）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;TCP发送端自己维护的【油门】，单位是MSS。这个参数决定了在没收到对方ACK之前，我最多能连续发多少个包。&lt;/p&gt;
&lt;p&gt;比如cwnd=10，就是最多连续发10个MSS大小的包，不用等ACK。&lt;br&gt;
需注意的是，cwnd不是固定的，网络越好、丢包越少，cwnd就越大，发送速度就越快；一旦丢包，就会马上降下来。&lt;/p&gt;
&lt;h4 id=&#34;ssthresh慢启动阈值slow-start-threshold&#34;&gt;&lt;strong&gt;ssthresh（慢启动阈值，Slow Start Threshold）&lt;/strong&gt;
&lt;/h4&gt;&lt;p&gt;cwnd的【换挡开关】，用来划分两个发送阶段，单位也是MSS。&lt;br&gt;
当cwnd ＜ ssthresh时，进入&lt;strong&gt;慢启动阶段&lt;/strong&gt;，cwnd每过一个RTT就直接翻倍，指数级快速涨速，尽快把带宽跑满；&lt;/p&gt;
&lt;p&gt;当cwnd ≥ ssthresh时，进入&lt;strong&gt;拥塞避免阶段&lt;/strong&gt;，cwnd每过一个RTT才加1，线性慢慢涨，避免冲太猛把网络堵死。&lt;br&gt;
一旦检测到网络拥堵丢包，ssthresh会立刻降到当前cwnd的一半，再根据丢包的严重程度调整cwnd，重新控制发送节奏。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【面试真题】讲讲JVM的垃圾回收机制？</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E8%AE%B2%E8%AE%B2jvm%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Sat, 14 Mar 2026 19:26:46 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E8%AE%B2%E8%AE%B2jvm%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;h2 id=&#34;为什么需要垃圾回收&#34;&gt;为什么需要垃圾回收？
&lt;/h2&gt;&lt;p&gt;首先得说清楚，JVM的垃圾回收（GC）到底是干嘛的。&lt;/p&gt;
&lt;p&gt;其实就像租房子住，时间长了会产生垃圾，要是不打扫，房子就没法住了。&lt;/p&gt;
&lt;p&gt;在Java里，我们写代码会创建很多对象（比如&lt;code&gt;new Person()&lt;/code&gt;、&lt;code&gt;new ArrayList()&lt;/code&gt;），这些对象都存在&lt;strong&gt;堆内存&lt;/strong&gt;里。&lt;/p&gt;
&lt;p&gt;但是，堆内存是有限的，要是对象用完了不清理，内存就会被占满，最后报&lt;code&gt;OutOfMemoryError&lt;/code&gt;（OOM），程序就挂了。&lt;/p&gt;
&lt;p&gt;所以GC的作用就是：&lt;strong&gt;自动找到堆里那些“没人用的垃圾对象”，把它们清理掉，腾出内存空间&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;堆内存划分&#34;&gt;堆内存划分
&lt;/h2&gt;&lt;p&gt;JVM把堆分成了新生代和老年代两大块：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;新生代&lt;/strong&gt;：刚创建的对象优先放在这里。就像“幼儿园”，大部分对象“朝生夕死”（比如方法里的临时变量，方法执行完就没用了）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;老年代&lt;/strong&gt;：在新生代里“熬过好几次GC”的对象，会被放到这里。就像“养老院”，这里的对象大多活得比较久（比如Spring容器里的单例Bean）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那为什么要这么划分呢？&lt;/p&gt;
&lt;p&gt;主要还是因为不同生命周期的对象，用不同的回收算法，效率会更高一点。&lt;/p&gt;
&lt;h3 id=&#34;各代用什么垃圾回收算法&#34;&gt;各代用什么垃圾回收算法？
&lt;/h3&gt;&lt;p&gt;我当时面试的时候突然懵了，说的不知道， 裂开。。。&lt;/p&gt;
&lt;p&gt;对新生代来说，它的特点是：&lt;strong&gt;垃圾多，存活对象少&lt;/strong&gt;（比如100个对象里，可能只有2个活着）。&lt;/p&gt;
&lt;p&gt;采用的是&lt;strong&gt;复制算法。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JVM把新生代分成三块：&lt;/p&gt;
&lt;p&gt;1个Eden区（占80%）和2个Survivor区（From和To，各占10%）。&lt;/p&gt;
&lt;p&gt;平时只用Eden区和其中一个Survivor区（比如From）。&lt;/p&gt;
&lt;p&gt;新对象先在Eden区分配，当Eden区满了，触发Minor GC（新生代GC），把Eden和From区里活着的对象，复制到To区，然后直接清空Eden和From区，From和To交换角色（下次用Eden和新的From区）。&lt;/p&gt;
&lt;p&gt;对老年代来说，它的特点是：&lt;strong&gt;存活对象多，垃圾少&lt;/strong&gt;，而且没有额外的大空间做复制担保，所以不用复制算法，采用的是**标记-清除 **或 &lt;strong&gt;标记-整理算法。&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&#34;标记-清除算法mark-sweep&#34;&gt;标记-清除算法（Mark-Sweep）
&lt;/h4&gt;&lt;p&gt;这种算法的实现逻辑是，从“GC Roots”（比如主线程、静态变量这些“根”）出发，找到所有活着的对象，做上标记，然后遍历堆内存，把没标记的对象（垃圾）清理掉。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt;是实现简单，但是会产生内存碎片（比如清理后剩下很多小空间，大对象可能放不下）。&lt;/p&gt;
&lt;h4 id=&#34;标记-整理算法mark-compact&#34;&gt;标记-整理算法（Mark-Compact）
&lt;/h4&gt;&lt;p&gt;为了解决内存碎片问题，在“标记-清除”基础上加了个“整理”步骤。&lt;/p&gt;
&lt;p&gt;实现逻辑和标记清除算法一样，先标记存活对象，把所有存活对象往内存的一端移动，按顺序排好，最后，直接清空边界以外的内存。&lt;/p&gt;
&lt;p&gt;这种算法的优点&lt;strong&gt;是&lt;/strong&gt;没有内存碎片，但缺点是需要移动对象，效率比“标记-清除”低。&lt;/p&gt;
&lt;h2 id=&#34;常见的垃圾回收器&#34;&gt;常见的垃圾回收器
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Serial&lt;/strong&gt;：单线程回收，适合客户端应用（比如桌面软件）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ParNew&lt;/strong&gt;：Serial的多线程版，常和CMS配合用；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CMS（Concurrent Mark Sweep）&lt;/strong&gt;：主打&lt;strong&gt;低延迟&lt;/strong&gt;（垃圾回收时尽量不让程序停顿太久），适合互联网应用（比如电商后台），用的是“标记-清除”算法；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;G1（Garbage-First）&lt;/strong&gt;：把堆分成很多小区域，优先回收垃圾多的区域，平衡了吞吐量和延迟。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>【面试真题】Java中什么是反射</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98java%E4%B8%AD%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%8D%E5%B0%84/</link>
        <pubDate>Sat, 14 Mar 2026 12:16:58 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98java%E4%B8%AD%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%8D%E5%B0%84/</guid>
        <description>&lt;p&gt;首先，官方的定义是这样的：&lt;/p&gt;
&lt;p&gt;Java反射（Reflection）是指在&lt;strong&gt;程序运行时&lt;/strong&gt;，动态获取类的完整结构信息（如构造方法、字段、方法），并能直接操作对象的属性或方法的&lt;strong&gt;机制&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;正射-vs-反射&#34;&gt;正射 vs 反射
&lt;/h3&gt;&lt;p&gt;用【创建对象并调用方法】举个例子：&lt;/p&gt;
&lt;h4 id=&#34;正射&#34;&gt;正射
&lt;/h4&gt;&lt;p&gt;就是我们平时写代码的方式，&lt;strong&gt;编译期&lt;/strong&gt;就知道要操作的类是什么，直接 &lt;code&gt;new&lt;/code&gt; 对象调用方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 正射：编译期就确定是User类&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 直接调用方法&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;反射&#34;&gt;反射
&lt;/h4&gt;&lt;p&gt;动态的方式**，编译期**不知道要操作的类是什么，&lt;strong&gt;运行期&lt;/strong&gt;才通过类名字符串动态获取类信息并操作：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 反射：运行期才通过字符串&amp;#34;com.example.User&amp;#34;获取类&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.User&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 动态创建对象&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setNameMethod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;setName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setNameMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 动态调用方法&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;原理&#34;&gt;原理
&lt;/h3&gt;&lt;p&gt;反射的底层，其实是操作JVM加载类后生成的 &lt;code&gt;Class&lt;/code&gt;&lt;strong&gt;对象&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先回顾一下类的加载过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;编译期&lt;/strong&gt;：&lt;code&gt;.java&lt;/code&gt;文件被编译成 &lt;code&gt;.class&lt;/code&gt;字节码文件；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类加载期&lt;/strong&gt;：JVM的类加载器（ClassLoader）把 &lt;code&gt;.class&lt;/code&gt;文件加载到内存，生成一个 &lt;code&gt;java.lang.Class&lt;/code&gt;对象（这个对象包含了类的所有结构信息）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行期&lt;/strong&gt;：反射就是通过这个 &lt;code&gt;Class&lt;/code&gt;对象，逆向获取类的构造方法、字段、方法等信息，并进行操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;说人话就是：&lt;code&gt;Class&lt;/code&gt;&lt;strong&gt;对象是反射的入口，所有反射操作都从获取&lt;/strong&gt;&lt;code&gt;Class&lt;/code&gt;&lt;strong&gt;对象开始&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;手撕一下加深理解&#34;&gt;手撕一下，加深理解
&lt;/h3&gt;&lt;h4 id=&#34;1-获取class对象的3种方式&#34;&gt;1. 获取Class对象的3种方式
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 方式1：类名.class（最常用，编译期就能获取）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 方式2：对象.getClass()（已有对象时用）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;extends&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 方式3：Class.forName(&amp;#34;全类名&amp;#34;)（最灵活，框架常用）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.User&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;2-操作构造方法动态创建对象&#34;&gt;2. 操作构造方法：动态创建对象
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.User&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取无参构造方法并创建对象&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Constructor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;noArgConstructor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;noArgConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取有参构造方法并创建对象&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Constructor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argConstructor&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;3-操作字段动态获取设置属性&#34;&gt;3. 操作字段：动态获取/设置属性
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.User&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取public字段并赋值&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Field&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nameField&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nameField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取private字段并赋值（需打破封装）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Field&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ageField&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;age&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ageField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setAccessible&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 关键：打破private访问限制&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ageField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;4-操作方法动态调用方法&#34;&gt;4. 操作方法：动态调用方法
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;com.example.User&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;newInstance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取public方法并调用&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setNameMethod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;setName&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setNameMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 获取private方法并调用（需打破封装）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getAgeMethod&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clazz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getDeclaredMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;getAge&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;getAgeMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setAccessible&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;age&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getAgeMethod&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;实际应用场景&#34;&gt;实际应用场景
&lt;/h3&gt;&lt;p&gt;Java生态里的&lt;strong&gt;核心框架几乎都用到了反射&lt;/strong&gt;，比如：&lt;/p&gt;
&lt;h4 id=&#34;1-spring框架的iocdi&#34;&gt;1. Spring框架的IOC/DI
&lt;/h4&gt;&lt;p&gt;Spring的IOC容器就是通过反射创建对象并注入依赖的：&lt;/p&gt;
&lt;p&gt;在XML里写 &lt;code&gt;&amp;lt;bean id=&amp;quot;user&amp;quot; class=&amp;quot;com.example.User&amp;quot;/&amp;gt;&lt;/code&gt;➡️Spring读取XML，拿到全类名 &lt;code&gt;com.example.User&lt;/code&gt;➡️Spring通过 &lt;code&gt;Class.forName()&lt;/code&gt; 获取Class对象，动态创建Bean对象➡️如果Bean有依赖（比如 &lt;code&gt;userService&lt;/code&gt; 依赖 &lt;code&gt;userDao&lt;/code&gt;），Spring再通过反射注入依赖。&lt;/p&gt;
&lt;h4 id=&#34;2-jdbc加载数据库驱动&#34;&gt;2. JDBC加载数据库驱动
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;Class.forName(&amp;quot;com.mysql.cj.jdbc.Driver&amp;quot;)&lt;/code&gt;，底层就是反射：首先加载驱动类到JVM，接下来驱动类的静态代码块会自动执行，把驱动注册到 &lt;code&gt;DriverManager&lt;/code&gt; 里。&lt;/p&gt;
&lt;h4 id=&#34;3-注解处理&#34;&gt;3. 注解处理
&lt;/h4&gt;&lt;p&gt;比如JUnit的 &lt;code&gt;@Test&lt;/code&gt; 注解：JUnit启动后，通过反射扫描所有类，找到带 &lt;code&gt;@Test&lt;/code&gt; 注解的方法，再通过反射动态调用这些方法执行测试。&lt;/p&gt;
&lt;h3 id=&#34;反射的优缺点&#34;&gt;反射的优缺点
&lt;/h3&gt;&lt;h4 id=&#34;优点&#34;&gt;优点
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;灵活性高&lt;/strong&gt;：运行期才确定要操作的类，不需要在编译期写死；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态性强&lt;/strong&gt;：适合开发通用框架（如Spring、MyBatis），框架不需要知道用户会写什么类，通过反射就能动态适配。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;缺点&#34;&gt;缺点
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;性能低&lt;/strong&gt;：反射比直接调用方法慢10-100倍（因为需要动态解析类信息），所以高频调用的场景（比如循环里）尽量不用反射；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;破坏封装性&lt;/strong&gt;：可以通过 &lt;code&gt;setAccessible(true)&lt;/code&gt; 访问private字段/方法，破坏了Java的封装原则；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全性问题&lt;/strong&gt;：反射可以执行任意代码，可能被恶意利用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;&#34;&gt;
&lt;/h2&gt;</description>
        </item>
        <item>
        <title>【面试真题】缓存和数据库的一致性问题如何保证？</title>
        <link>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E7%BC%93%E5%AD%98%E5%92%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81/</link>
        <pubDate>Fri, 13 Mar 2026 14:14:10 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E9%9D%A2%E8%AF%95%E7%9C%9F%E9%A2%98%E7%BC%93%E5%AD%98%E5%92%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81/</guid>
        <description>&lt;p&gt;坦白说，这个问题本质上不是个纯技术方案题，而是高并发场景下，平衡【性能、可用性、一致性强度】的工程题。&lt;/p&gt;
&lt;p&gt;核心是先看业务的一致性容忍度，再选对应方案，而且必须做好异常兜底，不然线上一定会出永久脏数据。&lt;/p&gt;
&lt;p&gt;在电商场景中，绝大多数情况下优先保证&lt;strong&gt;最终一致性&lt;/strong&gt;，只有金融级核心链路才会考虑&lt;strong&gt;强一致性&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先说&lt;strong&gt;标准流程&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读请求：先查缓存，命中直接返回；没命中就读数据库，把结果写回缓存再返回。&lt;/li&gt;
&lt;li&gt;写请求：先更新数据库，再删除缓存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;为什么是删除缓存而不是更新缓存&#34;&gt;为什么是【删除缓存】，而不是【更新缓存】？
&lt;/h3&gt;&lt;p&gt;一是并发写场景下，更新缓存一定会有数据覆盖的风险；&lt;/p&gt;
&lt;p&gt;二是电商场景很多缓存值是多表关联算出来的，更新缓存的成本极高，不如删除后懒加载；&lt;/p&gt;
&lt;p&gt;三是写多读少的场景，频繁更新缓存全是无效操作，浪费性能。&lt;/p&gt;
&lt;h3 id=&#34;为什么是先更库再删缓存不能反过来&#34;&gt;为什么是【先更库，再删缓存】，不能反过来？
&lt;/h3&gt;&lt;p&gt;如果你刚把缓存删了，数据库还没更新完，这时候高并发的&lt;strong&gt;读请求&lt;/strong&gt;进来，缓存查不到，直接去数据库拉了旧数据，还写回了缓存。&lt;/p&gt;
&lt;p&gt;等你数据库更新完，缓存里的旧数据就一直躺在那，除非缓存过期，不然用户一直看到的是错的数据。&lt;/p&gt;
&lt;p&gt;但是，如果是&lt;strong&gt;先更库再删缓存&lt;/strong&gt;，只有极小概率会出问题，比如：&lt;/p&gt;
&lt;p&gt;读请求缓存没命中，正在读数据库的瞬间，写请求更新了数据库并删了缓存，之后读请求把旧数据写回缓存。&lt;/p&gt;
&lt;p&gt;但是，但是啊，这个场景发生的条件太苛刻了，因为写数据库的耗时远大于读数据库，基本卡不中这个时间窗口，线上出问题的概率几乎可以忽略。&lt;/p&gt;
&lt;p&gt;就算真的撞上了，还有缓存 TTL 兜底，最多就是短时间不一致，绝对不会出永久脏数据。&lt;/p&gt;
&lt;p&gt;其实，这个方案最大的风险，是第二步删缓存失败。&lt;/p&gt;
&lt;p&gt;同步删缓存如果失败，立刻把这个key丢到&lt;strong&gt;消息队列&lt;/strong&gt;里做异步指数退避重试，设置最大重试次数，绝对不让缓存操作阻塞主流程。&lt;/p&gt;
&lt;h3 id=&#34;高并发下的两个进阶方案&#34;&gt;高并发下的两个进阶方案
&lt;/h3&gt;&lt;p&gt;第一个是延迟双删，解决基础方案的极端并发问题。&lt;br&gt;
“针对商品详情、营销活动这种超高并发读的场景，用延迟双删做加固，流程是：&lt;/p&gt;
&lt;p&gt;先删除缓存➡️更新数据库➡️延迟500ms-1s，二次删除缓存。&lt;br&gt;
第二次延迟删除，刚好能覆盖掉【数据库更新过程中，读请求写回缓存的旧数据】，彻底解决极端并发的脏数据问题。&lt;br&gt;
其中，延迟时间必须大于【业务读请求的最大耗时 + 数据库主从同步延迟】。&lt;/p&gt;
&lt;p&gt;第二个是基于Binlog的异步缓存淘汰。&lt;br&gt;
用Binlog异步淘汰缓存，完全解耦业务和缓存操作。&lt;/p&gt;
&lt;p&gt;业务代码只做一件事：更新数据库，完全不碰缓存。&lt;/p&gt;
&lt;p&gt;然后用Canal监听数据库的binlog增量日志，只有主库事务提交成功，才会拿到数据变更事件，再异步去删除对应的缓存key。如果删除失败，就丢进死信队列重试，超过阈值直接触发告警。&lt;/p&gt;
&lt;h3 id=&#34;强一致性方案&#34;&gt;强一致性方案
&lt;/h3&gt;&lt;p&gt;只有像支付账户余额、核心库存扣减这种，一分钱都不能错、零容错的场景，才会考虑强一致性方案，因为它的性能代价极大。&lt;br&gt;
用Redisson的分布式读写锁，写请求加写锁，读请求加读锁，读写互斥，强制串行化，彻底避免并发问题。&lt;/p&gt;
&lt;p&gt;但这个方案会严重降低系统吞吐量，高并发下很容易出现锁等待、超时问题，所以非核心场景，绝对不会用。&lt;/p&gt;
&lt;h3 id=&#34;兜底机制&#34;&gt;兜底机制
&lt;/h3&gt;&lt;p&gt;不管方案设计得多完美，线上一定会有意外，比如：&lt;/p&gt;
&lt;p&gt;缓存集群炸了、消息队列挂了、binlog消费延迟了，没有兜底，就等着出线上事故。&lt;br&gt;
可以采用以下兜底策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;TTL兜底&lt;/strong&gt;：所有缓存key必须设置合理的过期时间，哪怕所有机制都失效了，最多就是一段时间的不一致，缓存一过期，就自动拉取最新数据，绝对不会出现永久脏数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败重试机制&lt;/strong&gt;：所有缓存删除操作，必须有异步重试兜底，不能一次失败就放任脏数据存在。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;降级开关&lt;/strong&gt;：大促场景下，如果缓存集群扛不住了，直接开降级开关，所有请求切到读数据库，先保证用户能下单，别的都好说。&lt;/li&gt;
&lt;/ol&gt;
</description>
        </item>
        <item>
        <title>IDEA运行SpringBoot测试报错“命令行过长”？</title>
        <link>https://iamxurulin.github.io/p/idea%E8%BF%90%E8%A1%8Cspringboot%E6%B5%8B%E8%AF%95%E6%8A%A5%E9%94%99%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%BF%87%E9%95%BF/</link>
        <pubDate>Wed, 11 Feb 2026 12:44:33 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/idea%E8%BF%90%E8%A1%8Cspringboot%E6%B5%8B%E8%AF%95%E6%8A%A5%E9%94%99%E5%91%BD%E4%BB%A4%E8%A1%8C%E8%BF%87%E9%95%BF/</guid>
        <description>&lt;p&gt;在项目中想通过引入Redisson实现限流，运行&lt;code&gt;RedisLimiterManagerTest.doRateLimit&lt;/code&gt;测试方法时，弹出了这样的提示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/96e2189b38b54f8c8cca2fc30eae6297-fa7af53e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;原因分析&#34;&gt;原因分析
&lt;/h3&gt;&lt;p&gt;当项目依赖较多时，IDEA运行测试需要加载的类路径（各种依赖包的路径）会变得很长，而Windows对命令行参数的长度有默认限制（通常是8191字符），超出后就会触发“命令行过长”的错误。&lt;/p&gt;
&lt;h3 id=&#34;解决方法&#34;&gt;解决方法
&lt;/h3&gt;&lt;p&gt;1.点击IDEA右上角的&lt;strong&gt;运行配置下拉框&lt;/strong&gt; ➡️选择【编辑配置】，打开运行/调试配置窗口：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/827ba2dad0f34ec289db7ff99d4b91e4-cf924c31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;2.在配置窗口右侧，找到并点击【修改选项(M) Alt+M】按钮，弹出选项菜单后，选择 【缩短命令行】，此时界面会出现【缩短命令行】的下拉框，直接选择【JAR 清单】，这样的话IDEA会通过生成临时JAR清单来管理类路径。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/20e06a15059f49bb9b18103103ea516d-e976f2f3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;3.最后点击窗口右下角的【应用】➡️再点击【确定】，保存配置。&lt;/p&gt;
&lt;p&gt;4.重新运行&lt;code&gt;RedisLimiterManagerTest.doRateLimit&lt;/code&gt;测试方法，能正常执行并输出“成功”日志，命令行过长的问题解决~&lt;/p&gt;
</description>
        </item>
        <item>
        <title>怎么知道本地的Redis有没有设置密码</title>
        <link>https://iamxurulin.github.io/p/%E6%80%8E%E4%B9%88%E7%9F%A5%E9%81%93%E6%9C%AC%E5%9C%B0%E7%9A%84redis%E6%9C%89%E6%B2%A1%E6%9C%89%E8%AE%BE%E7%BD%AE%E5%AF%86%E7%A0%81/</link>
        <pubDate>Wed, 11 Feb 2026 11:50:41 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%80%8E%E4%B9%88%E7%9F%A5%E9%81%93%E6%9C%AC%E5%9C%B0%E7%9A%84redis%E6%9C%89%E6%B2%A1%E6%9C%89%E8%AE%BE%E7%BD%AE%E5%AF%86%E7%A0%81/</guid>
        <description>&lt;p&gt;进入到Redis的安装目录：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/cc6fa33f644e59bef9ec14ab8538fb48-a68cba51.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;双击【redis-server.exe】,如果双击之后窗口一闪而过， 检查一下默认的6379端口是否被占用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 6379 端口是否被占用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netstat -ano &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; findstr &lt;span class=&#34;m&#34;&gt;6379&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果输出类似下面这样的信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 类似这样的输出:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;TCP    127.0.0.1:6379    0.0.0.0:0    LISTENING    &lt;span class=&#34;m&#34;&gt;12345&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                      ↑这是进程ID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;说明 6379 端口已被进程占用，再检查是否已有 Redis 服务在运行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看 Redis 服务状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sc query Redis
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果输出：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SERVICE_NAME: Redis
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TYPE               : &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;  WIN32_OWN_PROCESS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        STATE              : &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;  RUNNING
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;说明Redis 已经作为 Windows 服务 在后台运行了，导致了端口被占用。&lt;/p&gt;
&lt;p&gt;接下来，直接使用已经运行的 Redis，直接双击安装目录下的【redis-cli.exe】，在弹出的窗口输入【config get requirepass】,如果以下的（2）为空，表示 没有设置密码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/57e3e7efb507f5f8bc9645df91d99303-7137055e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Redis、内存、缓存、MySQL、数据库，这些的区别到底是什么</title>
        <link>https://iamxurulin.github.io/p/redis%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%99%E4%BA%9B%E7%9A%84%E5%8C%BA%E5%88%AB%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88/</link>
        <pubDate>Sun, 01 Feb 2026 13:02:50 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/redis%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98mysql%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%99%E4%BA%9B%E7%9A%84%E5%8C%BA%E5%88%AB%E5%88%B0%E5%BA%95%E6%98%AF%E4%BB%80%E4%B9%88/</guid>
        <description>&lt;p&gt;这几个词经常一起出现，有次面试的时候被问到了，还是有点懵的，本文就来理清一下。&lt;/p&gt;
&lt;h2 id=&#34;1-内存memory&#34;&gt;1. 内存（Memory）
&lt;/h2&gt;&lt;p&gt;本质上是&lt;strong&gt;硬件&lt;/strong&gt;，比如电脑/服务器上的内存条，速度极快（纳秒级），缺点是断电会导致数据丢失。&lt;/p&gt;
&lt;p&gt;需要注意：程序运行时必须把数据加载到&lt;strong&gt;内存&lt;/strong&gt;才能执行。&lt;/p&gt;
&lt;h2 id=&#34;2-数据库database&#34;&gt;2. 数据库（Database）
&lt;/h2&gt;&lt;p&gt;本质上是&lt;strong&gt;软件&lt;/strong&gt;，用来持久化存储数据（断电不丢失），提供查询、修改、事务等能力，数据存在硬盘。&lt;/p&gt;
&lt;h2 id=&#34;3-mysql&#34;&gt;3. MySQL
&lt;/h2&gt;&lt;p&gt;本质上是&lt;strong&gt;关系型数据库&lt;/strong&gt;（ Relational Database Management System, RDBMS），数据存在硬盘，支持 SQL、事务、索引、复杂查询，速度比内存慢很多（毫秒级）。&lt;/p&gt;
&lt;p&gt;需要注意的是MySQL 是数据库的一种，但是&lt;strong&gt;数据库 ≠ MySQL&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;4-缓存cache&#34;&gt;4. 缓存（Cache）
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;本质上其实是一种“加速思想或者机制”，不是软件也不是硬件，而是一种&lt;/strong&gt;设计模式，主要用于把热点数据放在更快的地方，减少查询压力。&lt;/p&gt;
&lt;h2 id=&#34;5-redis&#34;&gt;5. Redis
&lt;/h2&gt;&lt;p&gt;本质上是基于内存的 &lt;strong&gt;NoSQL 数据库，数据主要存在&lt;/strong&gt;内存，也可以持久化到&lt;strong&gt;硬盘&lt;/strong&gt;，常被当作&lt;strong&gt;分布式缓存&lt;/strong&gt;使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;需要注意的是：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Redis 是一个数据库，Redis ≠ 缓存，只是它常被用来做缓存，但它功能远不止缓存，比如也可以做&lt;strong&gt;排行榜&lt;/strong&gt;、&lt;strong&gt;限流&lt;/strong&gt;、&lt;strong&gt;分布式锁&lt;/strong&gt;等等。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring Boot的多环境配置</title>
        <link>https://iamxurulin.github.io/p/spring-boot%E7%9A%84%E5%A4%9A%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/</link>
        <pubDate>Mon, 26 Jan 2026 15:27:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot%E7%9A%84%E5%A4%9A%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/</guid>
        <description>&lt;p&gt;在开发Spring Boot项目中，如果我们想把自己的项目开源到Github仓库，&lt;code&gt;application.yml&lt;/code&gt;中的某些配置比如&lt;code&gt;MySQL&lt;/code&gt;、&lt;code&gt;Redis&lt;/code&gt;的账户密码，还有的就是现在引入AI之后的一些密钥，可能都不太愿意推送到仓库去。&lt;/p&gt;
&lt;p&gt;这个时候，可以通过配置多环境来解决，给本地一个专有的配置文件，然后将其添加到&lt;code&gt;.gitignore&lt;/code&gt;文件中，这样就有效解决了这个问题。&lt;/p&gt;
&lt;p&gt;下面介绍一下 Spring Boot的多环境配置方法：&lt;/p&gt;
&lt;p&gt;Spring Boot 支持的多环境配置文件，命名规则为 &lt;code&gt;application-{profile}.yml&lt;/code&gt;（&lt;code&gt;profile&lt;/code&gt; 是环境标识，如&lt;code&gt;local/dev/test/prod&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id=&#34;操作步骤&#34;&gt;操作步骤
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;application.yml&lt;/code&gt;（公共配置）&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;spring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;application&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;xxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;profiles&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;active&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;local &lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# 激活local环境&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# springdoc-openapi（所有环境都用这个扫描包，通用）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;springdoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;group-configs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;packages-to-scan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;com.xx.xx.controller&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# knife4j（所有环境都开启，中文界面，通用）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;knife4j&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;setting&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;zh_cn&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;code&gt;application-local.yml&lt;/code&gt;（本地开发专属配置）&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 本地专属：端口、上下文路径&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;xxxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;servlet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;context-path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;/api&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 本地专属的其他配置（比如数据库、日志、Redis等）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# spring:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#   datasource:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#     url: jdbc:mysql://localhost:3306/xx?useSSL=false&amp;amp;serverTimezone=UTC&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#     username: xx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#     password: xx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#     driver-class-name: com.mysql.cj.jdbc.Driver&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# logging:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#   level:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#     com.xx.xx: debug  # 本地开发开启debug日志&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# redis:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#   host: localhost&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;#   port: xxxx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;通过在 &lt;code&gt;application.yml&lt;/code&gt; 中配置 &lt;code&gt;spring.profiles.active&lt;/code&gt; 激活指定环境，最后的加载顺序就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;先加载公共配置 &lt;code&gt;application.yml&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;再加载激活的环境配置 &lt;code&gt;application-local.yml&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样的话，环境配置中的&lt;strong&gt;同名配置项&lt;/strong&gt;会覆盖公共配置，&lt;strong&gt;不同配置项&lt;/strong&gt;自动合并，实现 “公共 + 专属” 并存。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>JDK版本的区别</title>
        <link>https://iamxurulin.github.io/p/jdk%E7%89%88%E6%9C%AC%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Mon, 26 Jan 2026 15:05:04 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/jdk%E7%89%88%E6%9C%AC%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;在IDEA中新建Spring Boot项目时，需要选择JDK版本，这里小记一下：&lt;/p&gt;
&lt;h3 id=&#34;oracle-openjdk&#34;&gt;Oracle OpenJDK
&lt;/h3&gt;&lt;p&gt;Oracle 官方维护的开源免费JDK，Java标准实现。&lt;/p&gt;
&lt;h3 id=&#34;amazon-corretto&#34;&gt;Amazon Corretto
&lt;/h3&gt;&lt;p&gt;亚马逊基于 OpenJDK 定制的免费、长期支持JDK。&lt;/p&gt;
&lt;h3 id=&#34;jetbrains-runtime&#34;&gt;JetBrains Runtime
&lt;/h3&gt;&lt;p&gt;JetBrains（IDEA/PyCharm 等 IDE 厂商）专为 IDE 自身运行定制的 JDK。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>【字节面试手撕】大数加法</title>
        <link>https://iamxurulin.github.io/p/%E5%AD%97%E8%8A%82%E9%9D%A2%E8%AF%95%E6%89%8B%E6%92%95%E5%A4%A7%E6%95%B0%E5%8A%A0%E6%B3%95/</link>
        <pubDate>Sun, 25 Jan 2026 22:48:06 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%AD%97%E8%8A%82%E9%9D%A2%E8%AF%95%E6%89%8B%E6%92%95%E5%A4%A7%E6%95%B0%E5%8A%A0%E6%B3%95/</guid>
        <description>&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/93f5ee0a70ab4078b5838868d4aff740-f90800df.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;求解代码&#34;&gt;求解代码
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;solve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 如果第一个字符串为空/长度为0，直接返回第二个字符串&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 如果第二个字符串为空/长度为0，直接返回第一个字符串&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;){&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义进位变量tmp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 定义StringBuilder拼接结果&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;StringBuilder&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;StringBuilder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 需要注意：最后一位相加仍有进位时，需把进位1也拼接到结果&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;charAt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;charAt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;j&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 取余10：得到当前位的计算结果&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;%&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 除以10：更新进位值&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 结果逆序：因为是从个位开始拼接，需要反转回正序，再转字符串返回&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;reverse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;toString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>MySQL中有哪些日志类型？</title>
        <link>https://iamxurulin.github.io/p/mysql%E4%B8%AD%E6%9C%89%E5%93%AA%E4%BA%9B%E6%97%A5%E5%BF%97%E7%B1%BB%E5%9E%8B/</link>
        <pubDate>Sat, 24 Jan 2026 15:38:05 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/mysql%E4%B8%AD%E6%9C%89%E5%93%AA%E4%BA%9B%E6%97%A5%E5%BF%97%E7%B1%BB%E5%9E%8B/</guid>
        <description>&lt;p&gt;MySQL有binlog、redo log和undo log三种日志，其中binlog 负责主从复制和数据恢复，redo log保证崩溃后数据不丢失，undo log支撑事务回滚和MVCC。&lt;/p&gt;
&lt;h3 id=&#34;1binlog&#34;&gt;1.binlog
&lt;/h3&gt;&lt;p&gt;binlog 是server层的日志，记录的是逻辑操作，也就是原始SQL或者行变更前后的值。&lt;/p&gt;
&lt;p&gt;它的核心场景是主从同步，从库拉取主库到binlog重放一遍就能保持数据一致。&lt;/p&gt;
&lt;p&gt;另外，做数据恢复的时候，也是靠binlog配合全量备份回放到指定时间点。&lt;/p&gt;
&lt;h3 id=&#34;2redo-log是innodb引擎独有的记录的是物理变更具体就是某个数据页的某个偏移量改成了什么值&#34;&gt;2.redo log是InnoDB引擎独有的，记录的是物理变更，具体就是“某个数据页的某个偏移量改成了什么值”。
&lt;/h3&gt;&lt;p&gt;它的作用是崩溃恢复（crash-safe），基于WAL（Write Ahead  Log）机制，MySQL挂了重启后，InnoDB会用redo log把没来得及刷盘的脏页恢复出来。&lt;/p&gt;
&lt;p&gt;redo log是循环写的，空间固定，写满了就得等checkpoint推进才能继续。&lt;/p&gt;
&lt;h3 id=&#34;3undo-log也是innodb引擎的逻辑日志记录的是数据修改前的旧值&#34;&gt;3.undo log也是InnoDB引擎的逻辑日志，记录的是数据修改前的旧值。
&lt;/h3&gt;&lt;p&gt;事务回滚的时候，就靠undo log把数据改回去，另外MVCC的快照读也依赖它，别的事务要读历史版本，顺着undo log链往前找就行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/84b73cf3efe84a6a8996ec343e4cf107-b0b792ea.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>MySQL是怎么实现事务的？</title>
        <link>https://iamxurulin.github.io/p/mysql%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E4%BA%8B%E5%8A%A1%E7%9A%84/</link>
        <pubDate>Fri, 23 Jan 2026 15:59:01 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/mysql%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E4%BA%8B%E5%8A%A1%E7%9A%84/</guid>
        <description>&lt;p&gt;MySQL实现事务靠的是4个核心组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Redo Log&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Undo Log&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;锁&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MVCC&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这四个组件分别对应事务的ACID特性的不同方面，一致性是由原子性、隔离性、持久性共同作用实现的。&lt;/p&gt;
&lt;p&gt;1.Redo Log保证持久性（D）&lt;/p&gt;
&lt;p&gt;在事务提交时，基于WAL（预写日志）机制将修改先写到Redo Log再刷盘写磁盘数据页。&lt;/p&gt;
&lt;p&gt;就算写数据页时宕机了，重启后通过重放redo log就能恢复数据，确保已提交的修改不会丢失。&lt;/p&gt;
&lt;p&gt;2.Undo Log保证原子性（A）&lt;/p&gt;
&lt;p&gt;每次修改数据前，先把原值以逻辑日志的形式存到undo log中。&lt;/p&gt;
&lt;p&gt;事务回滚时，按undo log反向操作把数据恢复回去。&lt;/p&gt;
&lt;p&gt;要么全做完，要么全撤销，不会出现改了一半的中间状态。&lt;/p&gt;
&lt;p&gt;3.锁机制保证隔离性（I）的写-写并发&lt;/p&gt;
&lt;p&gt;两个事务同时改同一行，必须一个等另一个释放锁，实现写-写互斥。&lt;/p&gt;
&lt;p&gt;InnoDB的锁粒度精确到行级（基于索引实现），还有间隙锁防止幻读。&lt;/p&gt;
&lt;p&gt;4.MVCC保证隔离性（I）的读-写并发&lt;/p&gt;
&lt;p&gt;读操作不加锁，通过undo log构建的版本链找到自己应该看到的数据版本，主要适配InnoDB默认的可重复读隔离级别RR。&lt;/p&gt;
&lt;p&gt;写的时候别人照样能读，读的时候别人照样能写，大幅度提升读写并发度。&lt;/p&gt;
&lt;p&gt;一致性不是单独实现的，它是&lt;strong&gt;原子性&lt;/strong&gt;、&lt;strong&gt;隔离性&lt;/strong&gt;和&lt;strong&gt;持久性&lt;/strong&gt;共同作用的结果。&lt;/p&gt;
&lt;p&gt;数据从一个正确状态转移到另一个正确状态，中间不会出现不一致的中间态，同时满足业务的约束规则。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/a2edac8b93cc4d5080d8731e9958cee3-c33deecc.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>解释一下为什么要有虚拟内存</title>
        <link>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8B%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E6%9C%89%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98/</link>
        <pubDate>Thu, 22 Jan 2026 19:18:56 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8B%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E6%9C%89%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98/</guid>
        <description>&lt;p&gt;虚拟内存的核心作用：&lt;/p&gt;
&lt;p&gt;● 扩展程序地址空间&lt;/p&gt;
&lt;p&gt;● 隔离进程地址空间&lt;/p&gt;
&lt;p&gt;● 简化内存管理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;虚拟内存允许程序运行在比实际物理内存更大的地址空间上。即使物理内存不足，系统也可以将不常用的页面移到磁盘的交换区，释放内存给活跃的程序使用，避免程序因内存不足而终止。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;操作系统通过页面表为每个内存页面设置访问权限，并由MMU在地址转换时校验权限。如果程序尝试非法访问，OS会拦截并触发异常，防止破坏其他程序或系统内核。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟内存将程序的逻辑地址与物理地址分离，一方面可以让程序员无需关心物理内存布局，简化开发；另一方面可以让进程拥有独立的虚拟地址空间，进程间无法直接访问，实现地址隔离，避免相互干扰。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>MyBatis Flex和MyBatis Plus的区别</title>
        <link>https://iamxurulin.github.io/p/mybatis-flex%E5%92%8Cmybatis-plus%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Thu, 22 Jan 2026 15:00:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/mybatis-flex%E5%92%8Cmybatis-plus%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;MyBatis Flex和MyBatis Plus都是对原生MyBatis 框架的增强工具，都能够&lt;strong&gt;简化数据库的操作&lt;/strong&gt;、&lt;strong&gt;提高开发效率。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;两者在设计理念和功能侧重点上有以下区别：&lt;/p&gt;
&lt;p&gt;1.MyBatis Flex除了MyBatis自身，没有任何第三方依赖，极致轻量化；而MyBatis Plus整合的功能更多（如逻辑删除、乐观锁、代码生成等），依赖也更复杂。&lt;/p&gt;
&lt;p&gt;2.MyBatis Flex从架构上进行了优化，在SQL执行的过程中没有SQL解析环节和MyBatis拦截器，通过 AST抽象语法树构建SQL，执行链路更短，可以带来更高的性能。而MyBatis Plus大量依赖MyBatis拦截器解析SQL，执行链路更长，有一定解析开销。&lt;/p&gt;
&lt;p&gt;3.MyBatis Flex原生支持多表关联查询，无需手写SQL，适合处理复杂的业务场景；而MyBatis Plus在多表查询时需要依赖第三方插件或者手写SQL来实现。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>如何在MySQL中监控和优化慢SQL？</title>
        <link>https://iamxurulin.github.io/p/%E5%A6%82%E4%BD%95%E5%9C%A8mysql%E4%B8%AD%E7%9B%91%E6%8E%A7%E5%92%8C%E4%BC%98%E5%8C%96%E6%85%A2sql/</link>
        <pubDate>Tue, 20 Jan 2026 18:04:18 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%A6%82%E4%BD%95%E5%9C%A8mysql%E4%B8%AD%E7%9B%91%E6%8E%A7%E5%92%8C%E4%BC%98%E5%8C%96%E6%85%A2sql/</guid>
        <description>&lt;p&gt;在MySQL中监控和优化慢SQL的核心是&lt;strong&gt;开启MySQL的慢查询日志&lt;/strong&gt;，慢查询日志会把执行时间&lt;strong&gt;超过设定阈值的SQL语句&lt;/strong&gt;自动记录下来，通过日志的分析与执行计划的校验实现SQL性能的精准优化。&lt;/p&gt;
&lt;p&gt;主要步骤：&lt;/p&gt;
&lt;p&gt;1.开启慢查询日志，设置合理的时间阈值，线上环境通常将阈值设定为1~2秒，确保执行耗时较长的慢SQL能够被精准捕获。&lt;/p&gt;
&lt;p&gt;2.定期对慢查询日志进行分析，用MySQL自带的分析工具&lt;code&gt;mysqldumpslow&lt;/code&gt;或Percona
Toolkit里的&lt;code&gt;pt-query-digest&lt;/code&gt;工具，对日志中的SQL进行汇总统计，定位高频出现的慢SQL。&lt;/p&gt;
&lt;p&gt;3.对高频的慢SQL执行EXPLAIN命令分析其执行计划，检查是否走索引、扫描数据行数、是否存在文件排序等问题，再针对分析结果开展针对性优化。&lt;/p&gt;
&lt;h3 id=&#34;几个常见的慢sql优化方向&#34;&gt;几个常见的慢SQL优化方向
&lt;/h3&gt;&lt;p&gt;1.查看EXPLAIN 结果的type字段，如果值为ALL，说明当前SQL是全表扫描；如果key列为空，就说明该SQL未使用索引，需优化索引配置。&lt;/p&gt;
&lt;p&gt;2.如果SELECT查询的字段没有包含在索引里，会触发回表查询，可以通过构建覆盖索引进行优化，把查询所需的字段都放进索引。&lt;/p&gt;
&lt;p&gt;3.查看EXPLAIN结果的Extra字段，如果是Using filesort，说明数据库执行了手动排序；如果是Using temporary，说明用了临时表；这两种情况都可以通过为对应字段添加索引来避免。&lt;/p&gt;
&lt;p&gt;4.避免用SELECT *查询所有字段，可以明确指定所需的字段；可以通过添加LIMIT子句控制单次查询返回的条数，从而减少数据处理压力。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么是RPC框架</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFrpc%E6%A1%86%E6%9E%B6/</link>
        <pubDate>Mon, 19 Jan 2026 17:43:23 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFrpc%E6%A1%86%E6%9E%B6/</guid>
        <description>&lt;p&gt;RPC（Remote Procdure Call）远程过程调用是一种用于实现在分布式系统中进行&lt;strong&gt;跨网络通信&lt;/strong&gt;的技术，也是一种计算机通信协议。&lt;/p&gt;
&lt;p&gt;RPC框架是基于RPC协议实现的。&lt;/p&gt;
&lt;p&gt;RPC允许一个程序（服务消费者）像调用自己程序的方法一样，调用另一个程序（服务提供者）的接口，无需关心数据的传输处理、数据编解码和底层网络通信的细节。&lt;/p&gt;
&lt;p&gt;这样一种功能的实现都是由&lt;strong&gt;RPC框架&lt;/strong&gt;来完成的，这就使得开发者能够更轻松地调用远程服务，快速开发分布式系统。&lt;/p&gt;
&lt;p&gt;RPC框架通常采用的是高效地网络通信协议（主流为性能远超HTTP的TCP协议）和高效轻量的序列化/反序列化机制；&lt;/p&gt;
&lt;p&gt;RPC框架提供的负载均衡、服务发现和容错机制（超时重试、熔断降级、限流等）可以提高系统的可靠性、可用性以及稳定性。&lt;/p&gt;
&lt;p&gt;除此之外，开发者还可以自定义负载均衡器和序列化协议，动态地扩展RPC的功能。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>MySQL中如何进行SQL调优</title>
        <link>https://iamxurulin.github.io/p/mysql%E4%B8%AD%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8Csql%E8%B0%83%E4%BC%98/</link>
        <pubDate>Mon, 19 Jan 2026 17:23:03 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/mysql%E4%B8%AD%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8Csql%E8%B0%83%E4%BC%98/</guid>
        <description>&lt;p&gt;SQL调优的核心思路是&lt;strong&gt;减少磁盘I/O&lt;/strong&gt;和&lt;strong&gt;避免无效计算&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;主要就是先通过MySQL的慢查询日志定位慢SQL，再利用EXPLAIN分析执行计划，最后再进行针对性优化。&lt;/p&gt;
&lt;p&gt;优化的手段主要有这几大类，分别是&lt;strong&gt;索引层面的优化&lt;/strong&gt;，&lt;strong&gt;SQL写法层面的优化&lt;/strong&gt;以及&lt;strong&gt;架构层面的优化&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;索引层面的优化&#34;&gt;索引层面的优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合理设计联合索引，利用覆盖索引来避免回表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注意最左匹配原则。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免在索引列上做函数运算、隐式类型转换。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;及时删除荣誉索引和重复索引。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;sql写法层面的优化&#34;&gt;SQL写法层面的优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;为了减少网络传输和内存占用，只对必要字段进行查询，不使用&lt;code&gt;select *&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;为了避免全表扫描，避免&lt;code&gt;%LIKE&lt;/code&gt;这种前缀模糊查询&lt;/li&gt;
&lt;li&gt;连表查询时检查&lt;strong&gt;关联字段&lt;/strong&gt;的字符集和排序规则是否一致，不一致会导致索引失效&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;架构层面的优化&#34;&gt;架构层面的优化
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;对于访问频率高但是变化少的数据使用Redis缓存&lt;/li&gt;
&lt;li&gt;对单表数据量超过2000万行或物理文件超过2G的大表进行分库分表&lt;/li&gt;
&lt;li&gt;搭建主从数据库集群，进行读写分离，让从库分担一些查询的压力&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>什么是Spring-Bean</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFspring-bean/</link>
        <pubDate>Mon, 19 Jan 2026 15:50:02 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFspring-bean/</guid>
        <description>&lt;p&gt;任何通过&lt;code&gt;Spring&lt;/code&gt; 容器&lt;strong&gt;实例化&lt;/strong&gt;、&lt;strong&gt;组装&lt;/strong&gt;和&lt;strong&gt;管理&lt;/strong&gt;的Java对象都能称之为&lt;strong&gt;Spring Bean&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Bean&lt;/code&gt;可以看成是&lt;code&gt;Spring&lt;/code&gt;应用中的一个普通&lt;strong&gt;Java对象&lt;/strong&gt;，这个对象的创建、属性赋值、初始化、销毁等完整的生命周期是由&lt;code&gt;Spring IOC&lt;/code&gt;容器来统一管理的，并非程序员手动控制。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Spring Bean&lt;/code&gt;的生命周期分为&lt;strong&gt;实例化&lt;/strong&gt;、&lt;strong&gt;依赖注入&lt;/strong&gt;、&lt;strong&gt;初始化&lt;/strong&gt;以及&lt;strong&gt;销毁&lt;/strong&gt;这4个固定不可逆的阶段。&lt;/p&gt;
&lt;p&gt;当&lt;code&gt;Spring&lt;/code&gt; 容器启动时，会根据配置文件或者注解，先实例化&lt;code&gt;Bean&lt;/code&gt;；&lt;/p&gt;
&lt;p&gt;之后，&lt;code&gt;Spring&lt;/code&gt; 容器再通过注解（&lt;code&gt;@Autowired&lt;/code&gt;）或者&lt;code&gt;setter&lt;/code&gt;方法将&lt;code&gt;Bean&lt;/code&gt;的依赖属性注入进来；&lt;/p&gt;
&lt;p&gt;依赖注入完成后，如果Bean标注了&lt;code&gt;@PostConstruct&lt;/code&gt;注解、实现了&lt;code&gt;InitializingBean&lt;/code&gt;接口或者配置了init-method，Spring就会调用相应的初始化方法，完成Bean的初始化操作。&lt;/p&gt;
&lt;p&gt;初始化完成后，Bean就进入到了就绪状态，可以被程序获取和使用了。&lt;/p&gt;
&lt;p&gt;如果Bean标注了&lt;code&gt;@PreDestroy&lt;/code&gt;注解、实现了&lt;code&gt;DisposableBean&lt;/code&gt;接口或者配置了&lt;code&gt;destroy-method&lt;/code&gt;，&lt;code&gt;Spring&lt;/code&gt;会在容器关闭时调用对应的销毁方法，完成&lt;code&gt;Bean&lt;/code&gt;的资源释放。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么是Spring-IOC</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFspring-ioc/</link>
        <pubDate>Sun, 18 Jan 2026 18:12:06 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFspring-ioc/</guid>
        <description>&lt;p&gt;Spring IOC（Inversion of Control）控制反转是将&lt;strong&gt;对象的创建权&lt;/strong&gt;和&lt;strong&gt;对象之间依赖关系的维护权&lt;/strong&gt;，从程序员的业务代码中转移到由Spring IOC容器中，程序员就不再手动控制了。&lt;/p&gt;
&lt;p&gt;需要说明的是，Spring IOC是一种&lt;strong&gt;设计思想&lt;/strong&gt;，不是一个具体的&lt;strong&gt;技术&lt;/strong&gt;，Spring IOC 的具体落地实现是通过&lt;strong&gt;依赖注入&lt;/strong&gt;（Dependency Injection） 完成的。&lt;/p&gt;
&lt;p&gt;依赖注入的方法有&lt;strong&gt;构造器注入&lt;/strong&gt;、&lt;strong&gt;setter注入&lt;/strong&gt;和&lt;strong&gt;接口注入&lt;/strong&gt;三种。&lt;/p&gt;
&lt;p&gt;那控制反转它&lt;strong&gt;控制&lt;/strong&gt;的是什么呢？&lt;/p&gt;
&lt;p&gt;其实就是控制的是&lt;strong&gt;对象的创建过程&lt;/strong&gt;，Spring IOC容器会根据&lt;strong&gt;配置文件&lt;/strong&gt;来创建对象。&lt;/p&gt;
&lt;p&gt;那&lt;strong&gt;反转&lt;/strong&gt;的是什么呢？&lt;/p&gt;
&lt;p&gt;反转的是&lt;strong&gt;创建对象&lt;/strong&gt;并且为这个&lt;strong&gt;要创建的对象&lt;/strong&gt;注入它所依赖的对象这个&lt;strong&gt;动作的主动权&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;传统开发过程是由程序员主动new一个对象，手动注入依赖，Spring IOC是将这个动作交给容器来自动完成，这样主动权就发生了&lt;strong&gt;反转&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;举个🌰说明一下：&lt;/p&gt;
&lt;p&gt;比如对象A依赖对象B，那在创建对象A的代码里，我们就需要写好应该如何创建对象B，只有这样才能创建一个完整的对象A。&lt;/p&gt;
&lt;p&gt;但是，反转之后，这个动作就会由Spring IOC容器去触发，容器在创建对象A的时候，发现对象A它依赖对象B，根据&lt;strong&gt;配置文件&lt;/strong&gt;，容器就会创建对象B，然后将对象B注入到对象A中。&lt;/p&gt;
&lt;p&gt;这里要注意一下，例子中的是注入一个&lt;strong&gt;对象&lt;/strong&gt;，其实还可以注入配置文件中的一个&lt;strong&gt;值、集合等等&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Java中的反射机制</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Sun, 18 Jan 2026 17:36:55 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;p&gt;反射机制是让程序在&lt;strong&gt;运行的时候&lt;/strong&gt;能够动态地&lt;strong&gt;获取&lt;/strong&gt;类的&lt;strong&gt;方法、字段、构造函数&lt;/strong&gt;这些结构信息，还能对其直接进行操作，这样，程序在编译时不需要知道具体的类型，等到&lt;strong&gt;运行时再决定&lt;/strong&gt;要调用哪个类和方法。&lt;/p&gt;
&lt;h2 id=&#34;反射机制主要有3大核心流程&#34;&gt;反射机制主要有3大核心流程：
&lt;/h2&gt;&lt;p&gt;获取Class对象➡️获取成员信息➡️操作目标对象。&lt;/p&gt;
&lt;h2 id=&#34;获取class对象的方式&#34;&gt;获取Class对象的方式：
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;1.全限定类名获取&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cla&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;forName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;xxx.xxx.MyClass&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;2.字面量获取&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cla&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MyClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;3.对象实例获取&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;?&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cla&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;getClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;获取成员信息&#34;&gt;获取成员信息
&lt;/h3&gt;&lt;p&gt;主要是从Class对象中获取字段Field、方法Method以及构造函数Constructor&lt;/p&gt;
&lt;h3 id=&#34;操作目标对象&#34;&gt;操作目标对象
&lt;/h3&gt;&lt;p&gt;主要是创建实例、读写字段和调用方法。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java中HashMap的默认负载因子为什么设置为0.75</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%ADhashmap%E7%9A%84%E9%BB%98%E8%AE%A4%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90%E4%B8%BA%E4%BB%80%E4%B9%88%E8%AE%BE%E7%BD%AE%E4%B8%BA0.75/</link>
        <pubDate>Sun, 18 Jan 2026 17:11:19 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%ADhashmap%E7%9A%84%E9%BB%98%E8%AE%A4%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90%E4%B8%BA%E4%BB%80%E4%B9%88%E8%AE%BE%E7%BD%AE%E4%B8%BA0.75/</guid>
        <description>&lt;p&gt;&lt;strong&gt;0.75&lt;/strong&gt;这个数字其实是一个时间和空间之间的平衡点。&lt;/p&gt;
&lt;p&gt;负载因子决定了&lt;strong&gt;什么时候会触发扩容&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;HashMap的扩容阈值=容量✖️负载因子。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;举个栗子：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;比如容量是16，负载因子是0.75，这样存到第12个元素的时候就会触发扩容；&lt;/p&gt;
&lt;p&gt;那如果负载因子改成0.5，这样存到第8个元素就会触发扩容；&lt;/p&gt;
&lt;p&gt;如果负载因子改成1，那这样只有存满16个元素才会触发扩容。&lt;/p&gt;
&lt;p&gt;所以，&lt;/p&gt;
&lt;p&gt;负载因子如果设置的太低，桶里的元素就会越稀疏，这样虽然查找速度会更快，但是会造成内存空间的浪费；&lt;/p&gt;
&lt;p&gt;负载因子如果设置的太高的话，桶里的元素就会越密集，虽然节省空间，但是冲突也会变多，这样查找就会变慢。&lt;/p&gt;
&lt;p&gt;0.75是一个折中的值，既不会让数组太过空旷，也不会让链表太长，在大多数场景下都能保持不错的性能。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>RabbitMQ怎么实现延迟消息</title>
        <link>https://iamxurulin.github.io/p/rabbitmq%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E5%BB%B6%E8%BF%9F%E6%B6%88%E6%81%AF/</link>
        <pubDate>Sat, 17 Jan 2026 18:19:27 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/rabbitmq%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E5%BB%B6%E8%BF%9F%E6%B6%88%E6%81%AF/</guid>
        <description>&lt;p&gt;在RabbitMQ中，实现延迟消息可以通过死信交换机（DLX）+过期时间（TTL）的方案，也可以采用RabbitMQ官方提供的延迟消息插件。&lt;/p&gt;
&lt;h3 id=&#34;dlxttl&#34;&gt;DLX+TTL。
&lt;/h3&gt;&lt;p&gt;主要步骤如下：&lt;/p&gt;
&lt;p&gt;1.声明并创建普通交换机和普通队列，完成二者的绑定。&lt;/p&gt;
&lt;p&gt;2.声明并创建死信交换机和死信队列，也完成二者的绑定。&lt;/p&gt;
&lt;p&gt;3.指定当前普通队列的死信要转发到哪个DLX，以及要匹配哪个死信路由键。&lt;/p&gt;
&lt;p&gt;4.不给普通队列配置任何的消费者，使普通队列只做消息暂存和过期，不消费。&lt;/p&gt;
&lt;p&gt;5.在生产者发送消息时，为消息设置TTL，将消息发送到普通队列和普通交换机。&lt;/p&gt;
&lt;p&gt;6当消息在普通队列的存活时间达到TTL后，过期的消息会被自动转发到DLX中，DLX再重新路由到死信队列，从死信队列中发送的消息就是延迟消息。&lt;/p&gt;
&lt;h3 id=&#34;rabbitmq_delayed_message_exchange插件&#34;&gt;rabbitmq_delayed_message_exchange插件
&lt;/h3&gt;&lt;p&gt;主要步骤：&lt;/p&gt;
&lt;p&gt;1.将插件放到RabbitMQ的plugins目录&lt;/p&gt;
&lt;p&gt;2.执行rabbitmq-plugins enable rabbitmq_delayed_message_exchange命令启用插件&lt;/p&gt;
&lt;p&gt;3.声明一个延迟交换机&lt;/p&gt;
&lt;p&gt;4.声明一个普通队列并绑定该延迟交换机&lt;/p&gt;
&lt;p&gt;5.生产者在发送消息时，设置消息投x-dlay:延迟时间(ms)&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Java中的JIT</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84jit/</link>
        <pubDate>Sat, 17 Jan 2026 18:18:18 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84jit/</guid>
        <description>&lt;p&gt;JIT（Just In Time）&lt;strong&gt;即时编译&lt;/strong&gt;，是一种在程序运行时将字节码转换为机器码的技术。&lt;/p&gt;
&lt;p&gt;JIT在Java程序运行的时候，如果发现了频繁执行的代码段，我们称之为热点代码，就会将这段热点代码编译成机器码，从而减少解释执行的开销。&lt;/p&gt;
&lt;p&gt;JIT有CLient Compiler和Server Compiler两种编译类型，&lt;/p&gt;
&lt;p&gt;其中，Client Compiler适用于客户端应用程序，主要用于快速启动的轻量级优化；&lt;/p&gt;
&lt;p&gt;Server Compiler适用于服务端应用程序，主要用于长时间运行的重度优化。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Java中的Logging-Write-Barrier</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84logging-write-barrier/</link>
        <pubDate>Sat, 17 Jan 2026 18:17:05 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84logging-write-barrier/</guid>
        <description>&lt;p&gt;Write Barrier可以理解为JVM在对象引用赋值操作中插入的一段AOP代码，通过这段代码，在应用程序运行期间，垃圾收集器可以感知到对象引用关系的变化，从而维护记忆集（RSet）、卡表（Card Table）等必要的数据结构，避免全堆扫描。&lt;/p&gt;
&lt;p&gt;Write Barrier按时机分，有Pre-Write Barrier和Post-Write Barrier两种类型。&lt;/p&gt;
&lt;p&gt;Pre-Write Barrier在对象引用改变前触发，用于记录引用变更前的状态；&lt;/p&gt;
&lt;p&gt;Post-Write Barrier和Pre-Write Barrier相反，是在对象引用改变后触发，用于记录引用变更后的状态。&lt;/p&gt;
&lt;p&gt;Logging是一种策略，在G1收集器中，Post和Pre Write Barrier都采用了Logging机制来实现，也就是先记录日志，后异步处理。&lt;/p&gt;
&lt;p&gt;那为什么要用&lt;code&gt;Logging&lt;/code&gt;这样一种策略呢？&lt;/p&gt;
&lt;p&gt;主要是如果每次引用修改都直接去更新RSet这种复杂的全局数据结构，这样会严重&lt;strong&gt;拖慢应用线程&lt;/strong&gt;，并可能会导致多线程下的&lt;strong&gt;伪共享问题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果采用&lt;code&gt;Logging&lt;/code&gt;机制，将对象的引用变更记录丢给&lt;strong&gt;应用线程负责&lt;/strong&gt;，让&lt;strong&gt;后台线程负责&lt;/strong&gt;复杂的处理，使对象的引用变更记录和处理解耦，从而保证在GC正确的前提下，最大程度地&lt;strong&gt;减少对应用程序吞吐量的影响&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java的CMS垃圾回收流程</title>
        <link>https://iamxurulin.github.io/p/java%E7%9A%84cms%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%B5%81%E7%A8%8B/</link>
        <pubDate>Fri, 16 Jan 2026 17:20:13 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E7%9A%84cms%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%B5%81%E7%A8%8B/</guid>
        <description>&lt;p&gt;&lt;strong&gt;1.初始标记（initial mark）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CMS会标记所有根对象直接可达的对象。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.并发标记（Concurrent marking）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;并发标记阶段，垃圾收集器和应用程序并发执行，从根对象直接可达的对象开始进行tracing，递归的扫描所有可达对象。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.并发预清理（Concurrent precleaning）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;并发预清理阶段会先分担一些重新标记阶段的工作，扫描卡表脏的区域以及新晋升到老年代的对象。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4.可中断的预清理阶段（AbortablePreclean）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;可中断的预清理阶段也是先分担一些重新标记阶段的工作，和并发预清理阶段不同的是这个阶段可以中断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5.重新标记（remark）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为前几个并发阶段会导致引用关系发生变化，所有需要重新遍历一遍新生代对象、GC Roots和卡表等，从而对标记进行修正，需要花较长的时间来进行重新扫描。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6.并发清理（Concurrent Sweeping）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;并发清理阶段，标记为不可达的对象会被清理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7.并发重置（Concurrent Reset）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;重置CMS内部的状态。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Linux系统中常用的日志文件</title>
        <link>https://iamxurulin.github.io/p/linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6/</link>
        <pubDate>Fri, 16 Jan 2026 17:19:26 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6/</guid>
        <description>&lt;p&gt;Linux系统中的日志文件通常都存储在&lt;code&gt;/var/log&lt;/code&gt;目录下。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;messages&lt;/th&gt;
          &lt;th&gt;记录大多数系统范围内的普通信息和错误消息&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;syslog&lt;/td&gt;
          &lt;td&gt;系统日志文件，记录系统范围内包括启动、内核事件设备信息等消息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;auth.log&lt;/td&gt;
          &lt;td&gt;记录授权相关的日志，比如用户的登录与登出&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;kern.log&lt;/td&gt;
          &lt;td&gt;记录内核产生的日志信息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;dmesg&lt;/td&gt;
          &lt;td&gt;记录设备和驱动程序的初始化信息，以及内核启动期间产生的消息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;boot.log&lt;/td&gt;
          &lt;td&gt;记录系统启动过程中发生的事件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;faillog&lt;/td&gt;
          &lt;td&gt;记录失败的登录尝试信息&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>Java中的继承机制</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Fri, 16 Jan 2026 16:29:52 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;p&gt;在Java中，允许子类复用父类的字段和方法，用&lt;code&gt;extends&lt;/code&gt;关键字建立继承关系。&lt;/p&gt;
&lt;p&gt;Java只支持&lt;strong&gt;单继承&lt;/strong&gt;，也就是一个类最多只能有一个直接父类。&lt;/p&gt;
&lt;p&gt;子类可以继承父类的&lt;code&gt;public&lt;/code&gt;和&lt;code&gt;protected&lt;/code&gt;成员；对于父类的&lt;code&gt;private&lt;/code&gt;成员，子类只能通过父类提供的&lt;code&gt;public/protected&lt;/code&gt;方法来间接地访问。&lt;/p&gt;
&lt;p&gt;子类的构造方法执行之前必须&lt;strong&gt;先调用父类的构造方法&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果父类存在&lt;strong&gt;无参数的构造方法&lt;/strong&gt;，则编译器会自动加上&lt;code&gt;super()&lt;/code&gt;;&lt;/p&gt;
&lt;p&gt;如果父类不存在无参数的构造方法，只存在带参数的构造方法，则子类必须显示地调用&lt;code&gt;super(参数)&lt;/code&gt;，因为编译器默认插入的是无参的&lt;code&gt;super()&lt;/code&gt;方法，会导致编译报错。&lt;/p&gt;
&lt;p&gt;继承在带来代码复用的同时也带来了紧耦合问题。&lt;/p&gt;
&lt;p&gt;子类在重写父类的方法时，只能放大或者保持方法的访问权限，不能缩小访问权限。&lt;/p&gt;
&lt;p&gt;也就是说，&lt;/p&gt;
&lt;p&gt;父类如果是&lt;code&gt;public&lt;/code&gt;方法，则子类只能是&lt;code&gt;public&lt;/code&gt;；&lt;/p&gt;
&lt;p&gt;父类如果是&lt;code&gt;protected&lt;/code&gt;方法，则子类可以是&lt;code&gt;protected&lt;/code&gt;或者&lt;code&gt;public&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;因为在父类引用指向子类对象时，调用方是期望能访问到这个方法的，如果子类缩小了这个权限，就会导致调用方无法访问。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说RabbitMQ的集群模式</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rabbitmq%E7%9A%84%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F/</link>
        <pubDate>Thu, 15 Jan 2026 17:21:36 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rabbitmq%E7%9A%84%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F/</guid>
        <description>&lt;p&gt;RabbitMQ的集群主要分为&lt;strong&gt;标准集群&lt;/strong&gt;、&lt;strong&gt;镜像集群&lt;/strong&gt;和&lt;strong&gt;联邦集群&lt;/strong&gt;三种。&lt;/p&gt;
&lt;h3 id=&#34;标准集群&#34;&gt;标准集群
&lt;/h3&gt;&lt;p&gt;标准集群是多台RabbitMQ服务器通过网络连接组成一个集群，所有节点共享元数据，但是一个消息只存储在单个节点上。&lt;/p&gt;
&lt;p&gt;在标准集群模式下，因为元数据是共享的，所以任何一个节点都知道消息在哪个节点上，可以实现负载均衡。&lt;/p&gt;
&lt;p&gt;消息存储在某个节点上但是不会自动复制到其他节点。&lt;/p&gt;
&lt;h3 id=&#34;镜像集群&#34;&gt;镜像集群
&lt;/h3&gt;&lt;p&gt;和标准集群模式不同，镜像集群模式下，队列的消息会复制到多个节点上，如果主队列所在的节点发生故障，副本节点会自动接管，从而保证队列的可用性。&lt;/p&gt;
&lt;h3 id=&#34;federated-集群&#34;&gt;Federated 集群
&lt;/h3&gt;&lt;p&gt;通过Federated Exchange或Federated Queue实现消息的跨节点路由 / 复制，节点之间无需共享元数据，解决了标准 / 镜像集群无法&lt;strong&gt;跨广域网部署&lt;/strong&gt;的问题。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux系统中如何使用ssh进行远程登录</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8ssh%E8%BF%9B%E8%A1%8C%E8%BF%9C%E7%A8%8B%E7%99%BB%E5%BD%95/</link>
        <pubDate>Wed, 14 Jan 2026 17:07:17 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8ssh%E8%BF%9B%E8%A1%8C%E8%BF%9C%E7%A8%8B%E7%99%BB%E5%BD%95/</guid>
        <description>&lt;p&gt;1.首先需要确保&lt;strong&gt;远程服务器&lt;/strong&gt;和&lt;strong&gt;本地主机&lt;/strong&gt;都安装了OpenSSH软件；&lt;/p&gt;
&lt;p&gt;2.如果采用用户名为&lt;code&gt;“Javaer_12”&lt;/code&gt;连接到远程主机&lt;code&gt;&amp;quot;192.168.1.114&amp;quot;&lt;/code&gt;，可以输入以下指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh Javaer_12@192.168.1.114
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;3.第一次连接的时候可能会提示你确认主机的真实性，输入&lt;code&gt;yes&lt;/code&gt;即可，再输入远程服务区的密码即可登录。&lt;/p&gt;
&lt;p&gt;⚠️ &lt;strong&gt;注意&lt;/strong&gt;：只能输完整的 &lt;code&gt;yes&lt;/code&gt;，输 &lt;code&gt;y&lt;/code&gt; 无效&lt;/p&gt;
&lt;p&gt;使用输入密码的方式进行登录，时间长了会感觉比较麻烦，这时可以考虑通过密钥验证的方式进行登录。&lt;/p&gt;
&lt;p&gt;通过密钥进行登录的大概就两步：&lt;/p&gt;
&lt;p&gt;先在本地生成ssh&lt;code&gt;密钥对&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh-keygen -t rsa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再将&lt;code&gt;公钥&lt;/code&gt;复制到远程服务器上：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh-copy-id Javaer_12@192.168.1.114
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样有时还是会感觉有点麻烦，可以使用&lt;code&gt;.ssh/config&lt;/code&gt;文件进行配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Host my_server_name
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	HostName 192.168.1.114
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	User Javaer_12
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接下来就可以通过输入&lt;code&gt;ssh my_server_name&lt;/code&gt;来进行登录了。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>整理在Linux系统中查看和管理系统服务的命令</title>
        <link>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%9F%A5%E7%9C%8B%E5%92%8C%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1%E7%9A%84%E5%91%BD%E4%BB%A4/</link>
        <pubDate>Wed, 14 Jan 2026 16:46:44 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%9F%A5%E7%9C%8B%E5%92%8C%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1%E7%9A%84%E5%91%BD%E4%BB%A4/</guid>
        <description>&lt;p&gt;在Linux系统中，可以使用&lt;code&gt;systemctl&lt;/code&gt;命令来查看和管理系统服务。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;code&gt;systemctl list-units --type=service&lt;/code&gt;&lt;/th&gt;
          &lt;th&gt;查看所有服务的状态&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl status &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;查看特定服务的状态&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl start &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;启动服务&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl stop &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;停止服务&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl restart &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;重启服务&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl enable &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;启用服务，设置为开机自启&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;systemctl disable &amp;lt;服务名&amp;gt;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;禁用服务，取消开机自启&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Linux 中服务名通常都是带&lt;code&gt;.service&lt;/code&gt;后缀的（如nginx.service、redis-server.service），使用systemctl时省略&lt;code&gt;.service&lt;/code&gt; 后缀，命令仍会生效。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么情况下会触发Java的Full-GC</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E4%BC%9A%E8%A7%A6%E5%8F%91java%E7%9A%84full-gc/</link>
        <pubDate>Wed, 14 Jan 2026 16:29:41 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E4%BC%9A%E8%A7%A6%E5%8F%91java%E7%9A%84full-gc/</guid>
        <description>&lt;p&gt;整理了以下5种可能会触发Java的Full GC的情况：&lt;/p&gt;
&lt;p&gt;1.当老年代的空间不足并且无法通过老年代的垃圾回收以释放足够多的空间时，会触发Full GC来回收老年代中的对象。&lt;/p&gt;
&lt;p&gt;2.在Java 8以前，如果永久代的空间不足，会直接触发 Full GC。
在Java 8及以后，永久代被元空间取代，由于元空间手动设置了最大阈值，当内存耗尽时也可能会触发Full GC。&lt;/p&gt;
&lt;p&gt;3.当&lt;strong&gt;显示&lt;/strong&gt;地调用&lt;code&gt;System.gc()&lt;/code&gt;方法时，JVM可能并不能保证立即执行Full GC，但是有可能会触发。&lt;/p&gt;
&lt;p&gt;4.当&lt;strong&gt;新生代的to区&lt;/strong&gt;放不下从eden区和from区拷贝过来的对象或者&lt;strong&gt;新生代的大对象或者长期存活的对象晋升&lt;/strong&gt;到老年代时，如果老年代没有足够的空间来容纳这些对象，这个时候也会触发Full GC。&lt;/p&gt;
&lt;p&gt;5.在要进行新生代GC的时候，如果根据之前的统计数据发现新生代的平均晋升大小比现在的老年代剩余空间还要大，也会触发 Full GC。&lt;/p&gt;
&lt;p&gt;为了&lt;strong&gt;减少Full GC 的触发&lt;/strong&gt;，可以考虑以下策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;调整堆内存的大小，从而减少老年代空间不足的情况&lt;/li&gt;
&lt;li&gt;增大新生代的大小，从而减少对象晋升到老年代的频率&lt;/li&gt;
&lt;li&gt;设置合理的元空间大小，从而避免元空间过小导致的频繁Full GC&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>请解释一下Java中的强引用、弱引用、软引用和虚引用</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B7%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Bjava%E4%B8%AD%E7%9A%84%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%92%8C%E8%99%9A%E5%BC%95%E7%94%A8/</link>
        <pubDate>Tue, 13 Jan 2026 16:57:03 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B7%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Bjava%E4%B8%AD%E7%9A%84%E5%BC%BA%E5%BC%95%E7%94%A8%E5%BC%B1%E5%BC%95%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E5%92%8C%E8%99%9A%E5%BC%95%E7%94%A8/</guid>
        <description>&lt;h3 id=&#34;强引用strong-reference&#34;&gt;强引用（Strong Reference）
&lt;/h3&gt;&lt;p&gt;这是最常见的引用类型。&lt;/p&gt;
&lt;p&gt;只要一个对象有强引用指向它，即便是系统内存紧张，垃圾回收器也&lt;strong&gt;不会回收&lt;/strong&gt;该对象。&lt;/p&gt;
&lt;h3 id=&#34;软引用soft-reference&#34;&gt;软引用（Soft Reference）
&lt;/h3&gt;&lt;p&gt;软引用是用来描述一些还有用但是并非必需的对象，通常用于实现缓存机制，允许程序在不影响性能的情况下利用多余的内存。&lt;/p&gt;
&lt;p&gt;当&lt;strong&gt;系统内存不足&lt;/strong&gt;时，垃圾回收器会对软引用指向的对象进行回收，避免内存溢出。&lt;/p&gt;
&lt;h3 id=&#34;弱引用weak-reference&#34;&gt;弱引用（Weak Reference）
&lt;/h3&gt;&lt;p&gt;弱引用是比软引用&lt;strong&gt;更弱&lt;/strong&gt;的一种引用类型，常用于防止内存泄露，允许缓存的键值对在不再使用的时候自动清除。&lt;/p&gt;
&lt;p&gt;和软引用在系统内存充足的情况下不会被回收不同的是，弱引用只要被垃圾回收器发现只有它指向某个对象时，&lt;strong&gt;不管系统内存是否充足&lt;/strong&gt;，这个对象都会被回收。&lt;/p&gt;
&lt;h3 id=&#34;虚引用phanton-reference&#34;&gt;虚引用（Phanton Reference）
&lt;/h3&gt;&lt;p&gt;虚引用是最弱的一种引用类型，主要就是用来跟踪对象的垃圾回收状态。&lt;/p&gt;
&lt;p&gt;如果一个对象只有虚引用，那么这个对象随时会被垃圾回收器回收。&lt;/p&gt;
&lt;p&gt;虚引用必须和引用队列（ReferenceQueue）配合使用，否则虚引用就毫无意义。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>解释一下Linux系统的权限管理机制</title>
        <link>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Blinux%E7%B3%BB%E7%BB%9F%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6/</link>
        <pubDate>Tue, 13 Jan 2026 16:28:41 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Blinux%E7%B3%BB%E7%BB%9F%E7%9A%84%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E6%9C%BA%E5%88%B6/</guid>
        <description>&lt;p&gt;Linux中的权限管理机制主要是围绕&lt;strong&gt;用户&lt;/strong&gt;和&lt;strong&gt;组&lt;/strong&gt;的权限展开。&lt;/p&gt;
&lt;p&gt;Linux中的文件和目录都是由：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;所有者（user）、组（Group）、其他人（Others）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这三种实体来&lt;strong&gt;管理权限&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;可以使用&lt;code&gt;ls -l&lt;/code&gt;命令来查看文件的&lt;strong&gt;权限&lt;/strong&gt;和&lt;strong&gt;所有者&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;1.每个文件和目录都有一个所有者和一个所属组。&lt;/p&gt;
&lt;p&gt;每个用户都有一个唯一的用户ID（UID），每个组也有一个ID（GID）。&lt;/p&gt;
&lt;p&gt;一个用户&lt;strong&gt;可以属于&lt;/strong&gt;多个组，但是一个文件&lt;strong&gt;只能&lt;/strong&gt;有一个所有者和一个组属性。&lt;/p&gt;
&lt;p&gt;2.权限可以使用&lt;strong&gt;符号&lt;/strong&gt;来表示，也可以使用&lt;strong&gt;八进制&lt;/strong&gt;来表示。&lt;/p&gt;
&lt;p&gt;可读（r，read）、可写（w，write）、可执行（x，execute）&lt;/p&gt;
&lt;p&gt;r对应八进制4，w对应八进制2，x对应八进制1，通过相加和可得出最终的权限数值。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux系统中-如何设置文件的权限和拥有者</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E6%9D%83%E9%99%90%E5%92%8C%E6%8B%A5%E6%9C%89%E8%80%85/</link>
        <pubDate>Mon, 12 Jan 2026 16:41:27 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E5%A6%82%E4%BD%95%E8%AE%BE%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E6%9D%83%E9%99%90%E5%92%8C%E6%8B%A5%E6%9C%89%E8%80%85/</guid>
        <description>&lt;p&gt;在Linux系统中，文件的权限可以通过&lt;code&gt;chmod&lt;/code&gt;指令来设置，文件的拥有者可以通过&lt;code&gt;chown&lt;/code&gt; 指令来设置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.设置文件的权限&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod &lt;span class=&#34;m&#34;&gt;755&lt;/span&gt; my_file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;设置my_file文件的权限为755&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.设置文件的拥有者&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chown user:group my_file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;将文件my_file的拥有者设置为user，所属的组设置为group&lt;/p&gt;
&lt;p&gt;在Linux中，文件权限以读（r）、写（w）、执行（x）三个基本权限来表示。&lt;/p&gt;
&lt;p&gt;权限分用户（u）、组（g）和其他用户（o）三类。&lt;/p&gt;
&lt;p&gt;755表示的是所有者拥有rwx权限，组合其他用户拥有rx权限，没有w权限。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置文件&lt;/strong&gt;一般设置为644，&lt;strong&gt;脚本文件&lt;/strong&gt;设置为755。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么是RabbitMQ中的死信队列</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFrabbitmq%E4%B8%AD%E7%9A%84%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97/</link>
        <pubDate>Mon, 12 Jan 2026 14:44:12 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFrabbitmq%E4%B8%AD%E7%9A%84%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97/</guid>
        <description>&lt;p&gt;在消息队列系统中，&lt;strong&gt;死信队列&lt;/strong&gt;（Dead Letter Queue,DLQ）是一种处理无法正常消费的消息的&lt;strong&gt;机制&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;RabbitMQ 中实现死信机制的核心是&lt;strong&gt;死信交换机&lt;/strong&gt;（Dead Letter Exchange, DLX），绑定该交换机的队列就是&lt;strong&gt;死信队列&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;死信队列是用来接收那些&lt;strong&gt;被拒绝的&lt;/strong&gt;、&lt;strong&gt;过期的&lt;/strong&gt;或者已经&lt;strong&gt;达到最大传递次数&lt;/strong&gt;的消息。&lt;/p&gt;
&lt;p&gt;可以通过给队列设置&lt;code&gt;x-dead-letter-exchange&lt;/code&gt; 参数来指定死信交换机，搭配&lt;code&gt;x-dead-letter-routing-key&lt;/code&gt;指定死信路由键，从而实现死信队列的绑定。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>怎么在RabbitMQ中配置消息的TTL</title>
        <link>https://iamxurulin.github.io/p/%E6%80%8E%E4%B9%88%E5%9C%A8rabbitmq%E4%B8%AD%E9%85%8D%E7%BD%AE%E6%B6%88%E6%81%AF%E7%9A%84ttl/</link>
        <pubDate>Mon, 12 Jan 2026 14:17:02 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%80%8E%E4%B9%88%E5%9C%A8rabbitmq%E4%B8%AD%E9%85%8D%E7%BD%AE%E6%B6%88%E6%81%AF%E7%9A%84ttl/</guid>
        <description>&lt;p&gt;TTL（Time To Live）表示&lt;strong&gt;消息在队列中存活的时间&lt;/strong&gt;，主要用于防止消息在队列中无限积压，导致系统资源的耗尽。&lt;/p&gt;
&lt;p&gt;配置TTL有两种方式，一种是&lt;strong&gt;队列级别的TTL&lt;/strong&gt;，另外一种是&lt;strong&gt;消息级别的TTL&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;1.在声明队列时通过设置&lt;code&gt;x-message-ttl&lt;/code&gt;参数来指定队列中所有消息的TTL。&lt;/p&gt;
&lt;p&gt;2.在发送消息时通过&lt;code&gt;AMQP.BasicProperties&lt;/code&gt;属性指定单个消息的TTL。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>RabbitMQ中的Prefetch参数</title>
        <link>https://iamxurulin.github.io/p/rabbitmq%E4%B8%AD%E7%9A%84prefetch%E5%8F%82%E6%95%B0/</link>
        <pubDate>Sun, 11 Jan 2026 17:07:58 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/rabbitmq%E4%B8%AD%E7%9A%84prefetch%E5%8F%82%E6%95%B0/</guid>
        <description>&lt;p&gt;在RabbitMQ中，prefetch参数主要是用于限制消费者端可以同时&lt;strong&gt;预取并未确认&lt;/strong&gt;消息的最大数量，帮助消费者端&lt;strong&gt;控制&lt;/strong&gt;消息处理的&lt;strong&gt;流量&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;举个例子说明一下：&lt;/p&gt;
&lt;p&gt;如果prefetch的参数设置为1，那么消费者会在未确认消息之前，只预取一个消息，这样可以确保每个消息在被处理成功之后，才接收下一个消息，从而避免消息的积压和资源耗尽。&lt;/p&gt;
&lt;p&gt;在&lt;strong&gt;负载均衡&lt;/strong&gt;、&lt;strong&gt;避免内存溢出&lt;/strong&gt;以及&lt;strong&gt;优先级处理&lt;/strong&gt;这些场景下会用到prefetch参数的设置。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;p&gt;1.RabbitMQ默认轮询分发消息，消费者可以根据其处理能力进行动态地调整prefetch值，实现基于处理能力的动态负载均衡。&lt;/p&gt;
&lt;p&gt;2.如果有大量未处理的消息堆积在内存中可能会导致内存溢出，这时通过设置一个较小的prefetch值可以避免这种情况。&lt;/p&gt;
&lt;p&gt;3.配合RabbitMQ的优先级队列，设置prefetch值可以确保队列优先推送高优先级的消息，让其被及时处理。&lt;/p&gt;
&lt;p&gt;注意⚠️：&lt;/p&gt;
&lt;p&gt;prefetch仅在&lt;strong&gt;消费者手动确认消息模式&lt;/strong&gt;下生效。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>JVM的内存区域是怎么划分的</title>
        <link>https://iamxurulin.github.io/p/jvm%E7%9A%84%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E6%98%AF%E6%80%8E%E4%B9%88%E5%88%92%E5%88%86%E7%9A%84/</link>
        <pubDate>Sun, 11 Jan 2026 16:36:51 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/jvm%E7%9A%84%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E6%98%AF%E6%80%8E%E4%B9%88%E5%88%92%E5%88%86%E7%9A%84/</guid>
        <description>&lt;p&gt;JVM的内存区域指的是&lt;strong&gt;JVM的运行时数据区&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;主要分为&lt;strong&gt;方法区&lt;/strong&gt;、&lt;strong&gt;堆&lt;/strong&gt;、&lt;strong&gt;虚拟机栈&lt;/strong&gt;、&lt;strong&gt;本地方法栈&lt;/strong&gt;和&lt;strong&gt;程序计数器&lt;/strong&gt;五个主要区域。&lt;/p&gt;
&lt;p&gt;1.方法区（Method Area）&lt;/p&gt;
&lt;p&gt;线程共享区域，存着类的结构信息、常量、静态变量。&lt;/p&gt;
&lt;p&gt;2.堆（Heap）&lt;/p&gt;
&lt;p&gt;最大的共享区，专门放对象和数组。&lt;/p&gt;
&lt;p&gt;3.虚拟机栈（JVM Stack）&lt;/p&gt;
&lt;p&gt;线程私有，存着局部变量、操作数栈、动态链接、方法出口信息、基本类型变量、对象引用。&lt;/p&gt;
&lt;p&gt;4.本地方法栈（Native Method Stack）&lt;/p&gt;
&lt;p&gt;线程私有，用来分配内存给非Java方法。&lt;/p&gt;
&lt;p&gt;类似于虚拟机栈，专为JNI调用本地代码服务。&lt;/p&gt;
&lt;p&gt;5.程序计数器（Program Counter Register）&lt;/p&gt;
&lt;p&gt;每个线程都有一个独立的程序计数器，线程私有。&lt;/p&gt;
&lt;p&gt;保存当前线程执行的Java 方法字节码指令的地方或者行号。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>JIT编译后的代码存在哪</title>
        <link>https://iamxurulin.github.io/p/jit%E7%BC%96%E8%AF%91%E5%90%8E%E7%9A%84%E4%BB%A3%E7%A0%81%E5%AD%98%E5%9C%A8%E5%93%AA/</link>
        <pubDate>Sun, 11 Jan 2026 16:04:17 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/jit%E7%BC%96%E8%AF%91%E5%90%8E%E7%9A%84%E4%BB%A3%E7%A0%81%E5%AD%98%E5%9C%A8%E5%93%AA/</guid>
        <description>&lt;p&gt;JIT（Just-In-Time）编译后的代码通常存放在代码缓存区（Code Cache）中。&lt;/p&gt;
&lt;p&gt;JVM提供了如下参数用于调整Code Cache的大小和行为：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;-XX:InitialCodeCacheSize&lt;/th&gt;
          &lt;th&gt;初始大小&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:ReservedCodeCacheSize&lt;/td&gt;
          &lt;td&gt;最大大小&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+PrintCodeCache&lt;/td&gt;
          &lt;td&gt;打印Code Cache信息&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Code Cache的默认大小依赖于JVM的版本和运行环境，通常有一个最大值。&lt;/p&gt;
&lt;p&gt;Code Cache分为多个区域，分别存储不同级别的编译代码：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;非方法代码&lt;/th&gt;
          &lt;th&gt;存储运行时的JVM调试代码或者模板代码&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;方法代码&lt;/td&gt;
          &lt;td&gt;存储普通JIT编译的代码&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;轮廓代码&lt;/td&gt;
          &lt;td&gt;存储优化级别更高的代码&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>说说Java中的常量池</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E5%B8%B8%E9%87%8F%E6%B1%A0/</link>
        <pubDate>Sat, 10 Jan 2026 17:02:58 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E5%B8%B8%E9%87%8F%E6%B1%A0/</guid>
        <description>&lt;p&gt;Java中的常量池是一块用于存储运行时常量或者符号的区域。&lt;/p&gt;
&lt;p&gt;主要有字符串常量池和运行时常量池。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符串常量池&lt;/strong&gt;用于存储字符串字面量，可以通过String类中的intern()方法复用常量池中的字符串对象，若不存在则将当前字符串对象入池。&lt;/p&gt;
&lt;p&gt;Java6中的字符串常量池位于方法区中的永久代中，Java7及以后位于堆内存中的特殊区域。&lt;/p&gt;
&lt;p&gt;在Java中，字符串的创建方式有两种，一种是直接使用&lt;strong&gt;字面量&lt;/strong&gt;，另一种是使用&lt;strong&gt;new关键字&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;运行时常量池&lt;/strong&gt;存储的是每个类或者接口的Class文件编译时生成的常量信息。&lt;/p&gt;
&lt;p&gt;需要说明的是，按照JVM的定义来说，&lt;strong&gt;字符串常量池&lt;/strong&gt;逻辑上还是属于&lt;strong&gt;运行时常量池&lt;/strong&gt;，只是因为字符串的高频使用，被单独抽离出来做了优化设计。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>RabbitMQ的消息确认机制是怎么工作的</title>
        <link>https://iamxurulin.github.io/p/rabbitmq%E7%9A%84%E6%B6%88%E6%81%AF%E7%A1%AE%E8%AE%A4%E6%9C%BA%E5%88%B6%E6%98%AF%E6%80%8E%E4%B9%88%E5%B7%A5%E4%BD%9C%E7%9A%84/</link>
        <pubDate>Sat, 10 Jan 2026 16:33:38 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/rabbitmq%E7%9A%84%E6%B6%88%E6%81%AF%E7%A1%AE%E8%AE%A4%E6%9C%BA%E5%88%B6%E6%98%AF%E6%80%8E%E4%B9%88%E5%B7%A5%E4%BD%9C%E7%9A%84/</guid>
        <description>&lt;p&gt;RabbitMQ的消息确认机制主要是用于确保消息的&lt;strong&gt;可靠传递，防止消息丢失&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;通过&lt;strong&gt;发布确认&lt;/strong&gt;和&lt;strong&gt;消费者确认&lt;/strong&gt;来实现。&lt;/p&gt;
&lt;h3 id=&#34;发布确认&#34;&gt;发布确认
&lt;/h3&gt;&lt;p&gt;当生产者发送消息到RabbitMQ时，可以选择开启发布确认模式，当RabbitMQ成功将消息入队/持久化之后，会发送一个ACK回复生产者，告知消息成功到达队列；
仅当消息处理失败时会返回NACK。&lt;/p&gt;
&lt;p&gt;如果生产者在合理的时间内没有收到ACK或者NACK，则可以&lt;strong&gt;重发消息&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;消费者确认&#34;&gt;消费者确认
&lt;/h3&gt;&lt;p&gt;消费者在处理完一条消息之后，必须发送一个ACK给RabbitMQ，告知RabbitMQ该消息已经处理完成，RabbitMQ在收到ACK之后会将该消息从队列中永久删除。&lt;/p&gt;
&lt;p&gt;如果RabbitMQ在收到消费者ACK之前检测到消费者已经断开连接，则认为该消息没有被成功处理，RabbitMQ会&lt;strong&gt;重新发送给其他消费者消费&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>整理Java中Object类的方法和作用</title>
        <link>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86java%E4%B8%ADobject%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%E5%92%8C%E4%BD%9C%E7%94%A8/</link>
        <pubDate>Sat, 10 Jan 2026 15:47:23 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86java%E4%B8%ADobject%E7%B1%BB%E7%9A%84%E6%96%B9%E6%B3%95%E5%92%8C%E4%BD%9C%E7%94%A8/</guid>
        <description>&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;public boolean equals(Object obj)&lt;/th&gt;
          &lt;th&gt;用来比较两个对象是不是相等，默认比较的是内存地址。&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;public int hashCode()&lt;/td&gt;
          &lt;td&gt;返回当前对象的哈希值&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public String toString()&lt;/td&gt;
          &lt;td&gt;返回对象的字符串形式&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public final Class&lt;?&gt; getClass()&lt;/td&gt;
          &lt;td&gt;返回对象运行时的类信息，反射的时候经常用到&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public void notify()&lt;/td&gt;
          &lt;td&gt;唤醒一个正在等待当前对象的线程。必须在synchronized代码块中配合wait()一起使用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public void notifyAll()&lt;/td&gt;
          &lt;td&gt;唤醒所有在这个对象上等待的线程&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public void wait()&lt;/td&gt;
          &lt;td&gt;当前线程挂起，等待别的线程来notify它，只能在同步块/同步方法中使用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public void wait(long timeout)&lt;/td&gt;
          &lt;td&gt;在wait方法的基础上加了个超时控制，过了时间就自己醒来&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;public void wait(long timeout,int nanos)&lt;/td&gt;
          &lt;td&gt;在wait()方法的基础上加了个更精细的超时控制，支持纳秒级的等待&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;protected Object clone()&lt;/td&gt;
          &lt;td&gt;拷贝当前对象，默认是浅拷贝&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;protected void finalize()&lt;/td&gt;
          &lt;td&gt;对象被垃圾回收之前会自动调用这个方法&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>Java中怎么判断对象是否是垃圾</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%AD%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E6%98%AF%E5%9E%83%E5%9C%BE/</link>
        <pubDate>Fri, 09 Jan 2026 16:00:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%AD%E6%80%8E%E4%B9%88%E5%88%A4%E6%96%AD%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E6%98%AF%E5%9E%83%E5%9C%BE/</guid>
        <description>&lt;p&gt;主要是采用&lt;strong&gt;引用计数法&lt;/strong&gt;和&lt;strong&gt;可达性分析算法&lt;/strong&gt;两种方式来实现。&lt;/p&gt;
&lt;h3 id=&#34;引用计数法reference-counting&#34;&gt;引用计数法（Reference Counting）
&lt;/h3&gt;&lt;p&gt;每个对象维护一个引用计数器，当引用计数增加时，计数器加1，减少时，计数器减1。&lt;/p&gt;
&lt;p&gt;当引用计数器为0时，说明该对象不再被引用，此时可以被回收。&lt;/p&gt;
&lt;p&gt;这种方法实现起来比较简单而且实时性好，但是无法处理循环引用的问题。&lt;/p&gt;
&lt;h3 id=&#34;可达性分析算法reachability-analysis&#34;&gt;可达性分析算法（Reachability Analysis）
&lt;/h3&gt;&lt;p&gt;这是Java中的垃圾回收主要采用的方法。&lt;/p&gt;
&lt;p&gt;通过一组成为“GC Roots”的对象出发，遍历所有可达的对象，对于一切无法通过GC Roots到达的对象，均可视为垃圾。&lt;/p&gt;
&lt;p&gt;这种方法可以解决循环引用的问题，但是需要消耗一定的资源进行标记。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux系统中-怎么检查系统的磁盘使用情况</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E6%80%8E%E4%B9%88%E6%A3%80%E6%9F%A5%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%A3%81%E7%9B%98%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</link>
        <pubDate>Fri, 09 Jan 2026 15:15:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E6%80%8E%E4%B9%88%E6%A3%80%E6%9F%A5%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%A3%81%E7%9B%98%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</guid>
        <description>&lt;p&gt;检查系统的磁盘使用情况可以使用&lt;code&gt;df&lt;/code&gt;和&lt;code&gt;du&lt;/code&gt;这两个命令。&lt;/p&gt;
&lt;h3 id=&#34;df命令&#34;&gt;df命令
&lt;/h3&gt;&lt;p&gt;df（disk filesystem），用于查看磁盘文件系统的整体使用情况。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;df -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;h表示human-readable，就是以人类可读的格式输出磁盘分区、已用空间、空闲空间、总空间和挂载点的信息。&lt;/p&gt;
&lt;h3 id=&#34;du命令&#34;&gt;du命令
&lt;/h3&gt;&lt;p&gt;du（disk usage），用于检查指定目录及其子目录的磁盘使用情况。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;du -sh /home/user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;-s显示目录的总大小，-h以人类可读的格式显示，合起来就是以人类可读的格式查看home/user目录的总大小。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;du -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;显示目录和所有文件的大小。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>解释一下NIO、BIO、AIO</title>
        <link>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Bniobioaio/</link>
        <pubDate>Fri, 09 Jan 2026 14:45:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Bniobioaio/</guid>
        <description>&lt;p&gt;NIO（Non-blocking I/O），&lt;strong&gt;非阻塞I/O模式&lt;/strong&gt;，调用方在发起I/O操作后即使操作未完成，也能立即返回。&lt;/p&gt;
&lt;p&gt;结合I/O多路复用技术，可以使一个线程同时管理多个连接。&lt;/p&gt;
&lt;p&gt;适用于连接数多、高并发和高性能要求的场景。&lt;/p&gt;
&lt;p&gt;BIO（Blocking I/O），传统的&lt;strong&gt;阻塞式I/O模式&lt;/strong&gt;，调用方在发起I/O操作时会被阻塞，直到操作完成后才会继续执行。&lt;/p&gt;
&lt;p&gt;适用于连接数较少，逻辑简单的场景。&lt;/p&gt;
&lt;p&gt;AIO（Asynchronous I/O）&lt;strong&gt;异步I/O模式&lt;/strong&gt;，调用方在发起I/O请求后，不需要轮询或者等待I/O操作完成，可以继续执行其他任务，操作系统或者底层库会在I/O操作完成后，通过回调或者事件通知的方式告知调用方。&lt;/p&gt;
&lt;p&gt;适用于对响应时间要求较高的应用场景。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>PO、VO、BO、DTO、DAO、POJO有什么区别</title>
        <link>https://iamxurulin.github.io/p/povobodtodaopojo%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Thu, 08 Jan 2026 17:58:10 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/povobodtodaopojo%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;PO（Persistent Object）持久化对象，主要用于和数据库交互，是数据库数据在内存中的镜像。&lt;/p&gt;
&lt;p&gt;VO（View Object）视图对象，和前端展示强相关，按需组装前端需要的字段。&lt;/p&gt;
&lt;p&gt;BO（Business Object）业务对象，封装业务逻辑，包含业务处理方法，是业务层专用，业务层只操作BO，不直接碰PO/DTO。&lt;/p&gt;
&lt;p&gt;DTO（Data Transfer Object）数据传输对象，屏蔽底层 PO 结构，可细分 ReqDTO（请求）和 ResDTO（响应），用于跨层或者跨服务传输数据。&lt;/p&gt;
&lt;p&gt;DAO（Data Access Object）数据访问对象，负责和数据库打交道，隔离业务逻辑和数据操作，依赖PO，通过操作PO完成与数据库的交互。&lt;/p&gt;
&lt;p&gt;POJO（Plain Ordinary Java Object）简单Java对象，最基础的Java类。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PO/VO/BO/DTO 本质上都是 POJO。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;常见的&lt;strong&gt;调用链路&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;前端请求 → Controller接收【ReqDTO】→ Service将ReqDTO转为【BO】→ BO调用【DAO】→ DAO操作【PO】与数据库交互 → Service将BO/PO转为【ResDTO/VO】→ Controller返回给前端&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在RabbitMQ中-怎么确保消息不会丢失</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8rabbitmq%E4%B8%AD-%E6%80%8E%E4%B9%88%E7%A1%AE%E4%BF%9D%E6%B6%88%E6%81%AF%E4%B8%8D%E4%BC%9A%E4%B8%A2%E5%A4%B1/</link>
        <pubDate>Thu, 08 Jan 2026 17:15:44 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8rabbitmq%E4%B8%AD-%E6%80%8E%E4%B9%88%E7%A1%AE%E4%BF%9D%E6%B6%88%E6%81%AF%E4%B8%8D%E4%BC%9A%E4%B8%A2%E5%A4%B1/</guid>
        <description>&lt;p&gt;为了确保消息不会丢失，可以从以下3个方面解决：&lt;/p&gt;
&lt;p&gt;1.在创建队列的时候设置durable为true，发布消息的时候设置delivery为2，从而确保队列和消息都是&lt;strong&gt;持久&lt;/strong&gt;的。&lt;/p&gt;
&lt;p&gt;这样，就算是RabbitMQ服务器重启也不会造成消息的丢失。&lt;/p&gt;
&lt;p&gt;2.开启&lt;strong&gt;发布确认模式&lt;/strong&gt;，这样的话，生产者会等待服务器的确认响应，确保消息已经成功存储。&lt;/p&gt;
&lt;p&gt;3.使用明确的&lt;strong&gt;消费者确认&lt;/strong&gt;机制，当消费者处理完消息之后，向RabbitMQ发送确认，只有在RabbitMQ收到消费者发来的确认之后才会将消息从队列中删除。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java中有哪些垃圾回收算法</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%AD%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95/</link>
        <pubDate>Thu, 08 Jan 2026 16:54:37 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%AD%E6%9C%89%E5%93%AA%E4%BA%9B%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95/</guid>
        <description>&lt;p&gt;Java中的垃圾回收算法主要有3种，分别是&lt;strong&gt;标记-清除算法&lt;/strong&gt;、&lt;strong&gt;复制算法&lt;/strong&gt;、&lt;strong&gt;标记-整理算法&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;1.标记-清除算法&lt;/p&gt;
&lt;p&gt;这种算法的逻辑其实很简单，就是先遍历一遍，把有用的东西都打个勾✅（标记），然后把那些没打勾的垃圾直接扔掉（清除）。&lt;/p&gt;
&lt;p&gt;不过，这种算法存在一个缺点，就是会留下&lt;strong&gt;内存碎片&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;2.复制算法&lt;/p&gt;
&lt;p&gt;复制算法可以很好地解决内存碎片问题，这种算法是把内存一分为二，平时只用一半。&lt;/p&gt;
&lt;p&gt;回收的时候，会把活着的对象全部复制到另一半去，然后把原来的那一半直接清空。&lt;/p&gt;
&lt;p&gt;这种算法的优点是快，可以保证没有碎片，但是需轮流着一半的空间不能用，太浪费空间了。&lt;/p&gt;
&lt;p&gt;3.标记-整理算法&lt;/p&gt;
&lt;p&gt;标记-整理算法是&lt;strong&gt;老年代&lt;/strong&gt;常用的算法。&lt;/p&gt;
&lt;p&gt;对老年代的对象，因为存活的时间长，如果采用复制算法，需要复制一大堆，速度太慢；&lt;/p&gt;
&lt;p&gt;但是，标记-清除又会产生碎片。&lt;/p&gt;
&lt;p&gt;所以，标记-整理算法是先进行标记，然后把所有活着的对象往一端推，再把剩下的空间全部清空。&lt;/p&gt;
&lt;p&gt;通过这种方式，既不会产生碎片，也不会浪费掉一半的空间，不过，把所有活着的对象往一端推这个整理的动作会比较耗时。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>整理Linux文件系统中各个目录的作用</title>
        <link>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86linux%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%90%84%E4%B8%AA%E7%9B%AE%E5%BD%95%E7%9A%84%E4%BD%9C%E7%94%A8/</link>
        <pubDate>Thu, 08 Jan 2026 16:25:43 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86linux%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%90%84%E4%B8%AA%E7%9B%AE%E5%BD%95%E7%9A%84%E4%BD%9C%E7%94%A8/</guid>
        <description>&lt;p&gt;Linux文件系统采用的是&lt;strong&gt;层次化&lt;/strong&gt;的&lt;strong&gt;树形结构&lt;/strong&gt;。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;/&lt;/th&gt;
          &lt;th&gt;根目录，系统的起点，包括所有其他目录&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;/bin&lt;/td&gt;
          &lt;td&gt;存放系统必需的二进制可执行文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/boot&lt;/td&gt;
          &lt;td&gt;存放引导加载程序和内核等系统启动相关的文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/dev&lt;/td&gt;
          &lt;td&gt;存放像硬盘、终端设备、虚拟机等存储设备文件，这些文件只是接口，不存储实际数据&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/etc&lt;/td&gt;
          &lt;td&gt;存放系统配置文件(用户账号)和脚本(启动脚本)等等&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/home&lt;/td&gt;
          &lt;td&gt;普通用户的主目录，每个用户会有一个单独的子目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/lib&lt;/td&gt;
          &lt;td&gt;必需的共享库和内核模块&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/media&lt;/td&gt;
          &lt;td&gt;挂载点，用于挂载像U盘和光盘这类可移除介质&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/mnt&lt;/td&gt;
          &lt;td&gt;一种临时挂载点，用于系统管理员手动挂载文件系统&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/opt&lt;/td&gt;
          &lt;td&gt;用于安装附加软件包，通常是第三方应用程序&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/proc&lt;/td&gt;
          &lt;td&gt;存放系统内核和进程信息的虚拟文件系统&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/root&lt;/td&gt;
          &lt;td&gt;root的主目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/sbin&lt;/td&gt;
          &lt;td&gt;存放系统管理员使用的系统二进制文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/tmp&lt;/td&gt;
          &lt;td&gt;临时文件目录，各种程序的临时存储空间&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/usr&lt;/td&gt;
          &lt;td&gt;包含用户执行的应用程序和文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;/var&lt;/td&gt;
          &lt;td&gt;存放日志文件、邮件等这类经常变化的文件&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>JVM由哪些部分组成</title>
        <link>https://iamxurulin.github.io/p/jvm%E7%94%B1%E5%93%AA%E4%BA%9B%E9%83%A8%E5%88%86%E7%BB%84%E6%88%90/</link>
        <pubDate>Wed, 07 Jan 2026 16:43:37 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/jvm%E7%94%B1%E5%93%AA%E4%BA%9B%E9%83%A8%E5%88%86%E7%BB%84%E6%88%90/</guid>
        <description>&lt;p&gt;JVM主要由&lt;strong&gt;类加载子系统&lt;/strong&gt;（ClassLoader）、&lt;strong&gt;运行时数据区&lt;/strong&gt;（Runtime Data Area）、&lt;strong&gt;执行引擎&lt;/strong&gt;（Execution Engine）以及&lt;strong&gt;本地方法接口&lt;/strong&gt;（Native Interface）4个部分组成。&lt;/p&gt;
&lt;p&gt;JVM就像一个虚拟的“电脑”，能让Java程序在不同的操作系统上跑起来。&lt;/p&gt;
&lt;p&gt;核心工作流程是：&lt;/p&gt;
&lt;p&gt;编写好的Java代码在编译成class文件之后，&lt;/p&gt;
&lt;p&gt;类加载器首先负责把class文件从磁盘或者网络中拉进来，放入到内存中；&lt;/p&gt;
&lt;p&gt;运行时数据区主要用于存放代码和变量；&lt;/p&gt;
&lt;p&gt;执行引擎像个翻译官，把Java的字节码转成机器码；&lt;/p&gt;
&lt;p&gt;当需要调用C++之类的外部代码时，就通过本地方法接口来帮忙桥接。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring-Boot支持哪些嵌入Web容器</title>
        <link>https://iamxurulin.github.io/p/spring-boot%E6%94%AF%E6%8C%81%E5%93%AA%E4%BA%9B%E5%B5%8C%E5%85%A5web%E5%AE%B9%E5%99%A8/</link>
        <pubDate>Wed, 07 Jan 2026 16:27:41 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot%E6%94%AF%E6%8C%81%E5%93%AA%E4%BA%9B%E5%B5%8C%E5%85%A5web%E5%AE%B9%E5%99%A8/</guid>
        <description>&lt;p&gt;Spring Boot支持以下4种嵌入Web容器：&lt;/p&gt;
&lt;p&gt;1.Tomcat&lt;/p&gt;
&lt;p&gt;Spring Boot默认的&lt;strong&gt;嵌入式Web容器&lt;/strong&gt;，Spring Boot会自动地将Tomcat内嵌到应用程序中，是一种广泛使用的轻量级、广泛使用的Servlet容器。&lt;/p&gt;
&lt;p&gt;2.Jetty&lt;/p&gt;
&lt;p&gt;Jetty比Tomcat更轻量，是一个&lt;strong&gt;高效的Web服务器和Servlet容器&lt;/strong&gt;，通常用于嵌入式系统或对资源占用较为敏感的环境。&lt;/p&gt;
&lt;p&gt;3.undertow&lt;/p&gt;
&lt;p&gt;undertow支持异步IO和HTTP/2，是一个轻量级的&lt;strong&gt;高性能Web服务器和Servlet容器&lt;/strong&gt;，适用于处理高并发的HTTP请求。&lt;/p&gt;
&lt;p&gt;4.Netty&lt;/p&gt;
&lt;p&gt;Netty是一个非阻塞的异步事件驱动框架，是Spring WebFlux的默认嵌入式容器，仅适用于响应式编程模型，适合高并发的响应式应用。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>你知道Spring-Boot配置文件的加载优先级吗</title>
        <link>https://iamxurulin.github.io/p/%E4%BD%A0%E7%9F%A5%E9%81%93spring-boot%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%8A%A0%E8%BD%BD%E4%BC%98%E5%85%88%E7%BA%A7%E5%90%97/</link>
        <pubDate>Wed, 07 Jan 2026 15:59:34 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BD%A0%E7%9F%A5%E9%81%93spring-boot%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E5%8A%A0%E8%BD%BD%E4%BC%98%E5%85%88%E7%BA%A7%E5%90%97/</guid>
        <description>&lt;p&gt;优先级如下：&lt;/p&gt;
&lt;p&gt;命令行参数&amp;gt; JAR&lt;strong&gt;包外&lt;/strong&gt; 的application-{profile}.properties&amp;gt; JAR &lt;strong&gt;包外&lt;/strong&gt;的application.properties&amp;gt;JAR&lt;strong&gt;包内&lt;/strong&gt;的application-{profile}.properties&amp;gt;JAR&lt;strong&gt;包内&lt;/strong&gt;的application.properties。&lt;/p&gt;
&lt;p&gt;需要&lt;strong&gt;注意&lt;/strong&gt;的是：&lt;/p&gt;
&lt;p&gt;当application.properties和application.yml同时存在时，如果是相同的参数，最终生效的是&lt;strong&gt;application.properties&lt;/strong&gt;中的配置。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux系统中-如何进行磁盘配额管理</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E7%A3%81%E7%9B%98%E9%85%8D%E9%A2%9D%E7%AE%A1%E7%90%86/</link>
        <pubDate>Wed, 07 Jan 2026 15:49:05 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD-%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E7%A3%81%E7%9B%98%E9%85%8D%E9%A2%9D%E7%AE%A1%E7%90%86/</guid>
        <description>&lt;p&gt;在Linux系统中进行磁盘配额管理的核心步骤：&lt;/p&gt;
&lt;p&gt;1.安装磁盘配额管理工具quota&lt;/p&gt;
&lt;p&gt;如果是Ubuntu/Debian系列的Linux系统，输入以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install quota
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果是CentOS/RHEL系列的Linux系统，输入以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo yum install quota
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;2.编辑/etc/fstab文件，为需要启用配额的分区添加usrquota和grpquota选项。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/dev/sda1 /home ext4 defaults,usrquota,grpquota &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;3.为了使第2步的配置生效，需要重新挂载分区&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo mount -o remount /home
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;4.生成配额文件aquota.user（用户配额文件）和aquota.group（组配额文件）并初始化配额数据库。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#c=创建配额文件，u=用户，g=组，m=强制检查，v=显示详细过程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo quotacheck -cugmv /home
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo quotaon /home
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;5.使用edquota命令为用户或组设置软性和硬性限制。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo edquota username
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>RabbitMQ的交换机有哪几种类型</title>
        <link>https://iamxurulin.github.io/p/rabbitmq%E7%9A%84%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E7%B1%BB%E5%9E%8B/</link>
        <pubDate>Tue, 06 Jan 2026 17:52:56 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/rabbitmq%E7%9A%84%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E7%B1%BB%E5%9E%8B/</guid>
        <description>&lt;p&gt;RabbitMQ是一个可实现异步通信和任务解耦的消息队列系统。&lt;/p&gt;
&lt;p&gt;主要有Direct、Fanout、Topic、Headers这4种类型。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Direct&lt;/th&gt;
          &lt;th&gt;根据消息的routing key精确匹配binding key，只有完全匹配的消息才会被转发到对应的Queue&lt;/th&gt;
          &lt;th&gt;适合像日志系统这种需要精确匹配的场景&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Fanout&lt;/td&gt;
          &lt;td&gt;不考虑routing key，直接将接收到的每一条消息都广播到所有绑定到它的Queue&lt;/td&gt;
          &lt;td&gt;适合广播消息，比如社交媒体的消息推送&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Topic&lt;/td&gt;
          &lt;td&gt;根据消息的routing key和binding key的模式匹配决定消息的流转路径&lt;/td&gt;
          &lt;td&gt;适合需要根据特定模式进行消息路由的场景，比如订单系统&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Headers&lt;/td&gt;
          &lt;td&gt;根据消息的头部属性来路由，提供更加复杂和灵活的路由策略&lt;/td&gt;
          &lt;td&gt;适合需要更复杂路由逻辑的场景，比如不同部门处理的邮件系统&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>操作系统的进程有哪几种状态</title>
        <link>https://iamxurulin.github.io/p/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E7%8A%B6%E6%80%81/</link>
        <pubDate>Tue, 06 Jan 2026 17:32:22 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%9C%89%E5%93%AA%E5%87%A0%E7%A7%8D%E7%8A%B6%E6%80%81/</guid>
        <description>&lt;p&gt;在操作系统层面，一个完整的生命周期中包含5种基础状态，分别是新建（New）、就绪（Ready）、运行（Running）、阻塞（Blocked/Waiting）、终止（Terminated）。这5种状态中，中间的就绪、运行、阻塞又是核心状态。&lt;/p&gt;
&lt;p&gt;我们可以把进程的这5种状态的转换想象成&lt;strong&gt;去银行的柜台办理业务&lt;/strong&gt;的流程：&lt;/p&gt;
&lt;p&gt;新建状态下，操作系统正在为进程分配进程ID（PID）和进程控制块（PCB），相当于我去银行取好了号，正在填写个人信息，但此时并没有进入到等待大厅；&lt;/p&gt;
&lt;p&gt;就绪状态就是只要CPU有空闲，进程就能马上执行，相当于我的材料这些都准备齐了，坐在等待大厅里等着叫号；&lt;/p&gt;
&lt;p&gt;运行状态下，进程此时占用了CPU的资源正在执行指令，相当于叫到我了，我此时就在柜台办理业务；&lt;/p&gt;
&lt;p&gt;阻塞状态就是进程执行到一半，需要等待某个I/O操作或者某个文件的输入完成后才能往下执行，此时就进入了阻塞状态，相当于业务办理到一半，发现我的身份证复印件没带，让我选择去附近打印室复印或者通知家人朋友送来，这个时候只能等复印件拿到手之后，才能重新排队继续办理，这时不能插队；&lt;/p&gt;
&lt;p&gt;终止状态，进程执行完毕或者发生了异常退出，资源被回收，相当于业务办理完了或者和柜员拌嘴被保安赶走，柜员可以给其他人接着办理了。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux系统中怎么排查文件占用问题</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%80%8E%E4%B9%88%E6%8E%92%E6%9F%A5%E6%96%87%E4%BB%B6%E5%8D%A0%E7%94%A8%E9%97%AE%E9%A2%98/</link>
        <pubDate>Tue, 06 Jan 2026 16:52:26 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%80%8E%E4%B9%88%E6%8E%92%E6%9F%A5%E6%96%87%E4%BB%B6%E5%8D%A0%E7%94%A8%E9%97%AE%E9%A2%98/</guid>
        <description>&lt;p&gt;平常的办公中，我们用的比较多的是Windows系统，应该也遇到过想删除一个文件但是显示这个文件被某个应用程序占用的问题吧，这个时候我们可以根据提示打开任务管理器把这个程序关闭，就能够删除这个文件了。&lt;/p&gt;
&lt;p&gt;那在Linux系统的命令行界面怎么排查文件的占用问题呢？&lt;/p&gt;
&lt;p&gt;可以使用&lt;code&gt;lsof&lt;/code&gt;命令。&lt;/p&gt;
&lt;p&gt;这个命令可以列出当前打开的文件以及关联的进程。&lt;/p&gt;
&lt;p&gt;1.如果需要查&lt;strong&gt;找某个特定文件&lt;/strong&gt;被哪个进程占用，可以执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsof /path/to/username/file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以上命令可以列出所有打开该文件的进程信息，像进程ID、用户、文件描述符等等。&lt;/p&gt;
&lt;p&gt;2.如果需要查找某个特定端口被哪个进程占用，可以执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsof -i :portnumber
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;3.如果想要查看某个特定用户打开了哪些文件，可以执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsof -u username
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;4.当你找到了占用文件的进程ID(PID)之后，可以使用kill命令来终止这个进程：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;kill&lt;/span&gt; -9 PID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>使用Linux的top命令进行性能监控的步骤</title>
        <link>https://iamxurulin.github.io/p/%E4%BD%BF%E7%94%A8linux%E7%9A%84top%E5%91%BD%E4%BB%A4%E8%BF%9B%E8%A1%8C%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7%E7%9A%84%E6%AD%A5%E9%AA%A4/</link>
        <pubDate>Tue, 06 Jan 2026 16:26:35 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BD%BF%E7%94%A8linux%E7%9A%84top%E5%91%BD%E4%BB%A4%E8%BF%9B%E8%A1%8C%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7%E7%9A%84%E6%AD%A5%E9%AA%A4/</guid>
        <description>&lt;p&gt;使用top命令可以获取到&lt;strong&gt;CPU使用率&lt;/strong&gt;、&lt;strong&gt;内存使用情况&lt;/strong&gt;、&lt;strong&gt;运行中的进程&lt;/strong&gt;等重要信息。&lt;/p&gt;
&lt;p&gt;步骤：&lt;/p&gt;
&lt;p&gt;1.在终端输入top命令，按Enter键&lt;/p&gt;
&lt;p&gt;2.之后就能看到一个实时更新的数据表，该数据表主要包含以下信息：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;系统信息&lt;/th&gt;
          &lt;th&gt;当前时间、系统运行时间、用户数和平均负载&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;进程摘要&lt;/td&gt;
          &lt;td&gt;总进程数、运行的进程数、睡眠的进程数&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;CPU状态&lt;/td&gt;
          &lt;td&gt;CPU的不同使用状态，比如：用户空间(us)、系统空间(sy)、空闲空间(id)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;内存和交换空间&lt;/td&gt;
          &lt;td&gt;物理内存和交换内存的使用情况&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;进程列表&lt;/td&gt;
          &lt;td&gt;当前所有运行进程的信息，包括进程ID(PID)、用户、优先级、CPU使用率、内存使用率等&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>常用的JVM配置参数</title>
        <link>https://iamxurulin.github.io/p/%E5%B8%B8%E7%94%A8%E7%9A%84jvm%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0/</link>
        <pubDate>Mon, 05 Jan 2026 15:48:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%B8%B8%E7%94%A8%E7%9A%84jvm%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0/</guid>
        <description>&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;-Xms&lt;/th&gt;
          &lt;th&gt;初始化堆内存大小&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;-Xmx&lt;/td&gt;
          &lt;td&gt;最大堆内存大小&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-Xss&lt;/td&gt;
          &lt;td&gt;设置每个线程的栈大小&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:MetaspaceSize&lt;/td&gt;
          &lt;td&gt;初始化元空间大小&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:MaxMetaspaceSize&lt;/td&gt;
          &lt;td&gt;最大元空间大小&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+HeapDumpOnOutOfMemoryError&lt;/td&gt;
          &lt;td&gt;当发生OOM时，生成堆转储&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+PrintGCDetails&lt;/td&gt;
          &lt;td&gt;打印详细的垃圾回收日志&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+UseG1GC&lt;/td&gt;
          &lt;td&gt;启用G1垃圾收集器&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+UseConcMarkSweepGC&lt;/td&gt;
          &lt;td&gt;启用CMS垃圾收集器&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;-XX:+UseZGC&lt;/td&gt;
          &lt;td&gt;启用ZGC低延迟垃圾收集器&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>说说RabbitMQ的基本架构</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rabbitmq%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%9E%B6%E6%9E%84/</link>
        <pubDate>Mon, 05 Jan 2026 15:37:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rabbitmq%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%9E%B6%E6%9E%84/</guid>
        <description>&lt;p&gt;RabbitMQ的架构有以下6个关键组件：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Producer&lt;/th&gt;
          &lt;th&gt;生产者&lt;/th&gt;
          &lt;th&gt;主要负责生产消息并发送给RabbitMQ&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Exchange&lt;/td&gt;
          &lt;td&gt;交换机&lt;/td&gt;
          &lt;td&gt;生产者发过来的消息并不是直接投进队列，而是 交给Exchange，让Exchange根据消息中的Routing Key来决定把它扔进哪个队列&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Queue&lt;/td&gt;
          &lt;td&gt;队列&lt;/td&gt;
          &lt;td&gt;这是消息最终存放的地方，在这里等待被取走&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Binding&lt;/td&gt;
          &lt;td&gt;绑定&lt;/td&gt;
          &lt;td&gt;把Exchange和Queue连接起来，告诉交换机这个消息需要扔进哪个队列&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Consumer&lt;/td&gt;
          &lt;td&gt;消费者&lt;/td&gt;
          &lt;td&gt;从Queue中取出消息并处理&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Virtual Host&lt;/td&gt;
          &lt;td&gt;虚拟主机&lt;/td&gt;
          &lt;td&gt;VHost之间是完全隔离的，互不影响，通常用于区分不同的业务线&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果把RabbitMQ比作&lt;strong&gt;快递物流系统&lt;/strong&gt;，那么6个关键组件分别对应的角色如下：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;生产者&lt;/strong&gt;是&lt;em&gt;发件人&lt;/em&gt;，&lt;strong&gt;交换机&lt;/strong&gt;是&lt;em&gt;快递分拣中心&lt;/em&gt;，&lt;strong&gt;队列&lt;/strong&gt;就是&lt;em&gt;快递仓库&lt;/em&gt;，&lt;strong&gt;绑定&lt;/strong&gt;就是&lt;em&gt;分拣规则表&lt;/em&gt;，&lt;strong&gt;消费者&lt;/strong&gt;就是&lt;em&gt;收件人&lt;/em&gt;，&lt;strong&gt;虚拟主机&lt;/strong&gt;就是&lt;em&gt;快递分公司&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;注意⚠️：&lt;/p&gt;
&lt;p&gt;一个消息可以被拷贝到多个Queue，但是一个队列里的消息只能被消费一次。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring-Bean一共有几种作用域</title>
        <link>https://iamxurulin.github.io/p/spring-bean%E4%B8%80%E5%85%B1%E6%9C%89%E5%87%A0%E7%A7%8D%E4%BD%9C%E7%94%A8%E5%9F%9F/</link>
        <pubDate>Mon, 05 Jan 2026 14:53:26 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-bean%E4%B8%80%E5%85%B1%E6%9C%89%E5%87%A0%E7%A7%8D%E4%BD%9C%E7%94%A8%E5%9F%9F/</guid>
        <description>&lt;p&gt;六种。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;singleton&lt;/th&gt;
          &lt;th&gt;默认是单例，一个 Spring IOC容器的内部只有一个Bean实例&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;prototype&lt;/td&gt;
          &lt;td&gt;原型，每次获取都会新建实例&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;request&lt;/td&gt;
          &lt;td&gt;每个请求都会新建一个属于自己的Bean实例&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;session&lt;/td&gt;
          &lt;td&gt;一个http session中有一个Bean的实例&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;application&lt;/td&gt;
          &lt;td&gt;整个ServletContext生命周期里，只有一个Bean实例&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;websocket&lt;/td&gt;
          &lt;td&gt;一个WebSocket生命周期内只有一个Bean实例&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;request、session、application、websocket这四种作用域都是只存在于Spring Web应用中的。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>整理一些可用来分析JVM性能的工具</title>
        <link>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E4%B8%80%E4%BA%9B%E5%8F%AF%E7%94%A8%E6%9D%A5%E5%88%86%E6%9E%90jvm%E6%80%A7%E8%83%BD%E7%9A%84%E5%B7%A5%E5%85%B7/</link>
        <pubDate>Sun, 04 Jan 2026 16:32:39 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E4%B8%80%E4%BA%9B%E5%8F%AF%E7%94%A8%E6%9D%A5%E5%88%86%E6%9E%90jvm%E6%80%A7%E8%83%BD%E7%9A%84%E5%B7%A5%E5%85%B7/</guid>
        <description>&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;jmap&lt;/th&gt;
          &lt;th&gt;一种用于生成&lt;strong&gt;堆转储&lt;/strong&gt;的命令行工具，可以用于分析JVM的内存使用情况&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;jstack&lt;/td&gt;
          &lt;td&gt;一种用于生成&lt;strong&gt;线程转储&lt;/strong&gt;的命令行工具，可以用于分析线程的状态&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;jstat&lt;/td&gt;
          &lt;td&gt;一种用于监控JVM统计信息的命令行工具，可提供实时的性能数据&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;MAT&lt;/td&gt;
          &lt;td&gt;一种用于&lt;strong&gt;分析&lt;/strong&gt;堆转储文件的工具，可以帮助识别内存泄漏和优化内存使用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;jconsole&lt;/td&gt;
          &lt;td&gt;可以监控JVM的内存使用，垃圾回收、线程、类加载等信息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;VisualVM&lt;/td&gt;
          &lt;td&gt;可实时显示JVM的内存使用、垃圾回收类加载等信息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Arthas&lt;/td&gt;
          &lt;td&gt;一个强大的Java诊断工具，提供实时监控和分析的功能&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>聊聊RabbitMQ</title>
        <link>https://iamxurulin.github.io/p/%E8%81%8A%E8%81%8Arabbitmq/</link>
        <pubDate>Sun, 04 Jan 2026 16:07:46 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%81%8A%E8%81%8Arabbitmq/</guid>
        <description>&lt;p&gt;RabbitMQ是一个开源的&lt;strong&gt;消息中间件&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果没有RabbitMQ，系统A直接把数据传给系统B，这样当B忙不过来或者挂了的时候，A就会卡死或者造成数据丢失。&lt;/p&gt;
&lt;p&gt;但是，如果有了RabbitMQ，系统A可以把数据传给RabbitMQ，而RabbitMQ则负责把数据暂存起来，再按照系统B的处理能力，稳稳地传送给B。&lt;/p&gt;
&lt;p&gt;RabbitMQ主要有3大应用场景：&lt;/p&gt;
&lt;p&gt;1.比如在实现用户注册功能时，通过引入RabbitMQ进行&lt;strong&gt;异步处理&lt;/strong&gt;，可以提升响应的速度：&lt;/p&gt;
&lt;p&gt;没有引入RabbitMQ时，在注册完之后，需要同步地发邮件和短信，这样用户可能需要等几秒钟才能看到“注册成功”。&lt;/p&gt;
&lt;p&gt;引入RabbitMQ之后，把注册的信息写入数据库之后，可以直接把“发送邮件”和“发送短信”这两个任务扔给RabbitMQ，这样就能够立刻给用户返回“注册成功”。&lt;/p&gt;
&lt;p&gt;2.我们平常网购的时候，应该也会注意到，买东西的次数多了，某宝或者某东、某多的积分也会相应增加。&lt;/p&gt;
&lt;p&gt;这也就是下单场景的积分累加。&lt;/p&gt;
&lt;p&gt;想象一下如果订单系统直接调用积分接口，一旦积分系统挂了，那订单也下不了了。&lt;/p&gt;
&lt;p&gt;有个大佬说过这样一句话：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;没有什么是加一层中间层不能解决的，如果有，那就再加一层。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;所以，中间加个RabbitMQ，这样订单系统只需要把“我要下单”的消息发送给RabbitMQ，而积分系统什么时候恢复那就什么时候再去取消息，从而累加积分。&lt;/p&gt;
&lt;p&gt;通过这样引入RabbitMQ，可以实现&lt;strong&gt;应用之间的解耦&lt;/strong&gt;，从而降低系统的依赖。&lt;/p&gt;
&lt;p&gt;3.在双十一的秒杀场景下，通常会有几百上千万的请求在一瞬间涌进来，这个时候数据库可能就直接崩了。&lt;/p&gt;
&lt;p&gt;通过引入RabbitMQ，先把请求全部扔进RabbitMQ排队，后台系统再按照自己的节奏从RabbitMQ中拉取请求进行处理，从而实现&lt;strong&gt;流量削峰&lt;/strong&gt;，保护数据库的作用。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>你给我解释一下Linux系统中的内存泄露问题和排查的方法</title>
        <link>https://iamxurulin.github.io/p/%E4%BD%A0%E7%BB%99%E6%88%91%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Blinux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E9%97%AE%E9%A2%98%E5%92%8C%E6%8E%92%E6%9F%A5%E7%9A%84%E6%96%B9%E6%B3%95/</link>
        <pubDate>Sun, 04 Jan 2026 15:30:40 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BD%A0%E7%BB%99%E6%88%91%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8Blinux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E9%97%AE%E9%A2%98%E5%92%8C%E6%8E%92%E6%9F%A5%E7%9A%84%E6%96%B9%E6%B3%95/</guid>
        <description>&lt;p&gt;在计算机程序中，当不再被使用的内存没有被程序释放，随着时间的推移，这些内存会逐渐累积起来，最后导致系统内存不足，这就是所谓的&lt;strong&gt;内存泄漏&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;内存泄漏轻则导致程序性能下降，重则导致系统崩溃。&lt;/p&gt;
&lt;p&gt;那么排查内存泄漏则有以下方法：&lt;/p&gt;
&lt;p&gt;1.使用top或者htop命令监视系统的整体内存使用情况，来帮助识别具体哪个进程占用了过多的内存。&lt;/p&gt;
&lt;p&gt;2.使用free命令查看系统内存的使用情况，包括总内存、已用内存、空闲内存、缓存内存。&lt;/p&gt;
&lt;p&gt;3.使用ps命令列出进程的详细信息，包括内存的使用情况。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Spring-Boot-Actuator</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4spring-boot-actuator/</link>
        <pubDate>Sun, 04 Jan 2026 14:33:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4spring-boot-actuator/</guid>
        <description>&lt;p&gt;Spring Boot Actuator是一个具有监控和管理功能的工具。&lt;/p&gt;
&lt;p&gt;通过Actuator可以监控应用的健康状况、查看指标、跟踪请求、管理环境参数等等。&lt;/p&gt;
&lt;p&gt;Actuator的优势：&lt;/p&gt;
&lt;p&gt;1.开箱即用，可以很容易地与外部监控系统集成。&lt;/p&gt;
&lt;p&gt;2.提供了大量预定义的端点，可以用于展示和操作应用的内部状态，并且还支持自定义端点，扩展现有的端点功能。&lt;/p&gt;
&lt;p&gt;3.提供了关于应用、系统、JVM、内存、线程等丰富的信息。&lt;/p&gt;
&lt;p&gt;4.支持端点的安全访问控制，可以限制哪些端点可以公开访问。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>整理一些Linux的常用命令</title>
        <link>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E4%B8%80%E4%BA%9Blinux%E7%9A%84%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</link>
        <pubDate>Sat, 03 Jan 2026 16:40:32 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%95%B4%E7%90%86%E4%B8%80%E4%BA%9Blinux%E7%9A%84%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</guid>
        <description>&lt;h3 id=&#34;文件管理&#34;&gt;文件管理
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;ls&lt;/th&gt;
          &lt;th&gt;列出目录的内容&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;cd&lt;/td&gt;
          &lt;td&gt;切换目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;cp&lt;/td&gt;
          &lt;td&gt;复制文件或者目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;mv&lt;/td&gt;
          &lt;td&gt;移动或者重命名文件或者目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;rm&lt;/td&gt;
          &lt;td&gt;删除文件或者目录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;touch&lt;/td&gt;
          &lt;td&gt;创建空文件或者更新文件的时间戳&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;find&lt;/td&gt;
          &lt;td&gt;查找文件或者目录&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;权限管理&#34;&gt;权限管理
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;chmod&lt;/th&gt;
          &lt;th&gt;修改文件或者目录的权限&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;chown&lt;/td&gt;
          &lt;td&gt;修改文件或者目录的所有者&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;chgrp&lt;/td&gt;
          &lt;td&gt;修改文件或者目录的所属组&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;文本处理&#34;&gt;文本处理
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;cat&lt;/th&gt;
          &lt;th&gt;查看文件的内容&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;grep&lt;/td&gt;
          &lt;td&gt;搜索文本的内容&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;sed&lt;/td&gt;
          &lt;td&gt;流编辑器，主要用于文本的处理&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;awk&lt;/td&gt;
          &lt;td&gt;文本处理工具，适用于处理列数据&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;系统管理&#34;&gt;系统管理
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;ps&lt;/th&gt;
          &lt;th&gt;显示当前的进程&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;top&lt;/td&gt;
          &lt;td&gt;动态地显示系统资源的使用情况&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;df&lt;/td&gt;
          &lt;td&gt;显示文件系统的磁盘空间使用情况&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;du&lt;/td&gt;
          &lt;td&gt;显示文件或者目录的磁盘空间使用情况&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;kill&lt;/td&gt;
          &lt;td&gt;终止进程&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;shutdown&lt;/td&gt;
          &lt;td&gt;关机或者重启系统&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;网络管理&#34;&gt;网络管理
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;ping&lt;/th&gt;
          &lt;th&gt;检测网络的连通性&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;ifconfig/ip&lt;/td&gt;
          &lt;td&gt;配置网络接口&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;netstat&lt;/td&gt;
          &lt;td&gt;显示网络连接、路由表等信息&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ssh&lt;/td&gt;
          &lt;td&gt;远程登录&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;scp&lt;/td&gt;
          &lt;td&gt;安全复制文件&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;压缩和解压&#34;&gt;压缩和解压
&lt;/h3&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;tar&lt;/th&gt;
          &lt;th&gt;打包和解包文件&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;gzip&lt;/td&gt;
          &lt;td&gt;压缩和解压文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;zip/unzip&lt;/td&gt;
          &lt;td&gt;压缩和解压文件&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        </item>
        <item>
        <title>Java对象是怎么在虚拟机中存储的</title>
        <link>https://iamxurulin.github.io/p/java%E5%AF%B9%E8%B1%A1%E6%98%AF%E6%80%8E%E4%B9%88%E5%9C%A8%E8%99%9A%E6%8B%9F%E6%9C%BA%E4%B8%AD%E5%AD%98%E5%82%A8%E7%9A%84/</link>
        <pubDate>Sat, 03 Jan 2026 16:19:53 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E5%AF%B9%E8%B1%A1%E6%98%AF%E6%80%8E%E4%B9%88%E5%9C%A8%E8%99%9A%E6%8B%9F%E6%9C%BA%E4%B8%AD%E5%AD%98%E5%82%A8%E7%9A%84/</guid>
        <description>&lt;p&gt;Java对象在虚拟机中主要由&lt;strong&gt;对象头、对象实例和对齐填充&lt;/strong&gt;三部分组成。&lt;/p&gt;
&lt;p&gt;1.对象头中包含了对象的元信息和运行时数据。&lt;/p&gt;
&lt;p&gt;对象头主要由Mark Word、类型指针和数组长度三部分组成。&lt;/p&gt;
&lt;p&gt;其中，数组长度只有数组才有；&lt;/p&gt;
&lt;p&gt;Mark Word主要用于存储运行时数据，会根据对象的状态动态变化；&lt;/p&gt;
&lt;p&gt;类型指针指向对象对应的类的元数据，用于确定该对象的类型。&lt;/p&gt;
&lt;p&gt;2.对象实例存储的是对象的实际数据，也就是类的字段。&lt;/p&gt;
&lt;p&gt;3.为了满足内存一般情况下的8字节对齐要求，JVM可能会在对象的末尾添加填充字节。&lt;/p&gt;
&lt;p&gt;至于存储位置，大多数的对象分配在&lt;strong&gt;堆&lt;/strong&gt;中，堆也是JVM管理的内存中最大的一块区域。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Linux-CFS</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4linux-cfs/</link>
        <pubDate>Sat, 03 Jan 2026 15:53:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4linux-cfs/</guid>
        <description>&lt;p&gt;Linux CFS（ Completely Fair Scheduler，全公平调度器 ）是一种用于替代O(1)调度器的进程调度算法。&lt;/p&gt;
&lt;p&gt;CFS主要目的是使每个任务都能够按照其优先级，占用CPU的时间片段，尽可能公平地分配CPU资源。&lt;/p&gt;
&lt;p&gt;CFS有以下特点：&lt;/p&gt;
&lt;p&gt;1.每个任务都是按照&lt;strong&gt;优先级&lt;/strong&gt;决定权重，进而根据权重分配CPU时间，尽量保证每个任务都能按比例公平地获得CPU资源。&lt;/p&gt;
&lt;p&gt;2.使用&lt;strong&gt;虚拟运行时间&lt;/strong&gt;（vruntime）来衡量每个任务的CPU时间，vruntime越低，优先级越高。&lt;/p&gt;
&lt;p&gt;3.所有可运行的任务都存储在&lt;strong&gt;红黑树&lt;/strong&gt;中，按照vruntime进行排序，其中最左节点也就是vruntime最小的节点更优先获得CPU的调度。&lt;/p&gt;
&lt;p&gt;4.CFS支持多核调度，能够很好地处理从&lt;strong&gt;嵌入式系统&lt;/strong&gt;到&lt;strong&gt;服务器集群&lt;/strong&gt;的各种应用场景。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring是怎么解决循环依赖的</title>
        <link>https://iamxurulin.github.io/p/spring%E6%98%AF%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84/</link>
        <pubDate>Sat, 03 Jan 2026 15:15:01 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring%E6%98%AF%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E7%9A%84/</guid>
        <description>&lt;p&gt;Spring是通过&lt;strong&gt;三级缓存&lt;/strong&gt;机制来解决循环依赖的。&lt;/p&gt;
&lt;p&gt;我们可以把这三级缓存想象成3个不同等级的“货架”：&lt;/p&gt;
&lt;p&gt;其中，一级缓存存放的是&lt;strong&gt;成品货架&lt;/strong&gt;，这里面都是完全初始化好，并且可以直接使用的Bean。&lt;/p&gt;
&lt;p&gt;二级缓存存放的是&lt;strong&gt;半成品货架&lt;/strong&gt;，这里面都是已经实例化好的，但是呢，还没有填充属性的Bean。&lt;/p&gt;
&lt;p&gt;三级缓存存放的是&lt;strong&gt;工厂货架&lt;/strong&gt;，这里放的就不是Bean了，而是一个可以生产Bean的工厂。&lt;/p&gt;
&lt;p&gt;我们来理一下三级缓存解决循环依赖的流程：&lt;/p&gt;
&lt;p&gt;假设A和B是相互依赖的，&lt;/p&gt;
&lt;p&gt;首先，Spring先把A实例化出来，此时A还只是一个空壳，紧接着把一个可以获取A的工厂放到三级缓存里。&lt;/p&gt;
&lt;p&gt;然后，A开始填充属性，这时发现需要B，所以就跑去创建B。&lt;/p&gt;
&lt;p&gt;这时，B也实例化出来了，也开始填充属性，发现需要A。&lt;/p&gt;
&lt;p&gt;然后，B就去一级缓存中寻找A，但是呢，没找到；所以又去二级缓存中寻找，也没找到；再跑去三级缓存中寻找，这下找到了。&lt;/p&gt;
&lt;p&gt;这时，B就调用三级缓存中的工厂，拿到了A的引用。&lt;/p&gt;
&lt;p&gt;为了保证是单例，只生产一次，B就把拿到的A放到二级缓存中，并把三级缓存中的工厂删掉。&lt;/p&gt;
&lt;p&gt;B拿到了A后，B就创建完成了，入驻一级缓存。&lt;/p&gt;
&lt;p&gt;A拿到B之后，A也可以创建完成，也可以入驻一级缓存。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Java程序的执行流程</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B/</link>
        <pubDate>Fri, 02 Jan 2026 16:32:54 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B/</guid>
        <description>&lt;p&gt;Java程序的执行流程：&lt;/p&gt;
&lt;p&gt;1.编写.java源代码文件。&lt;/p&gt;
&lt;p&gt;2.使用javac编译器生成.class字节码文件。&lt;/p&gt;
&lt;p&gt;3.通过java命令启动JVM，并指定主类。&lt;/p&gt;
&lt;p&gt;4.JVM类加载器按需加载主类及运行所需的其他.class文件。&lt;/p&gt;
&lt;p&gt;5.JVM定位到主类的main方法，开始执行其逻辑，作为程序的入口。&lt;/p&gt;
&lt;p&gt;6.执行过程中，JVM通过解释执行和JIT即时编译，将字节码转换为机器码并执行。&lt;/p&gt;
&lt;p&gt;7.运行期间，JVM对内存进行管理，回收不再使用的对象。&lt;/p&gt;
&lt;p&gt;8.如果没有非守护线程运行，则触发JVM退出流程。&lt;/p&gt;
&lt;p&gt;9.JVM销毁内部组件，释放资源，最终JVM进程退出。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>如何在Linux系统中查看某个特定进程的内存使用情况</title>
        <link>https://iamxurulin.github.io/p/%E5%A6%82%E4%BD%95%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%9F%A5%E7%9C%8B%E6%9F%90%E4%B8%AA%E7%89%B9%E5%AE%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</link>
        <pubDate>Fri, 02 Jan 2026 15:53:05 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%A6%82%E4%BD%95%E5%9C%A8linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%9F%A5%E7%9C%8B%E6%9F%90%E4%B8%AA%E7%89%B9%E5%AE%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%86%85%E5%AD%98%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</guid>
        <description>&lt;p&gt;1.使用ps命令&lt;/p&gt;
&lt;p&gt;举个例子，如果要查看进程ID为1527的进程的内存使用情况，可以使用如下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ps -p &lt;span class=&#34;m&#34;&gt;1527&lt;/span&gt; -o %mem,%cpu,vsz,rss
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其中，vsz表示虚拟内存的大小，rss表示常驻内存的大小。&lt;/p&gt;
&lt;p&gt;2.使用top命令&lt;/p&gt;
&lt;p&gt;要查看某个进程的内存使用情况时，可以依次输入top，再按f键进入域选择界面，再按N键就能显示内存的使用排序。&lt;/p&gt;
&lt;p&gt;3.使用/proc文件系统&lt;/p&gt;
&lt;p&gt;/proc提供了内核和进程的信息。&lt;/p&gt;
&lt;p&gt;每个进程都有一个对应的目录，以ID为1527的进程为例，&lt;/p&gt;
&lt;p&gt;/proc/1527/status包含VmSize虚拟内存大小和VmRSS常驻内存大小这些基本信息；&lt;/p&gt;
&lt;p&gt;/proc/1527/statm包含进程的内存页面数。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>JVM方法区会出现内存溢出吗</title>
        <link>https://iamxurulin.github.io/p/jvm%E6%96%B9%E6%B3%95%E5%8C%BA%E4%BC%9A%E5%87%BA%E7%8E%B0%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA%E5%90%97/</link>
        <pubDate>Fri, 02 Jan 2026 15:19:31 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/jvm%E6%96%B9%E6%B3%95%E5%8C%BA%E4%BC%9A%E5%87%BA%E7%8E%B0%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA%E5%90%97/</guid>
        <description>&lt;p&gt;在Java8以前，方法区被实现在永久代中，永久代是一块固定大小地内存区域，这块区域不能进行动态地扩展。所以，如果加载的类过多或者常量池的数据过多，超出了永久代的限制，就会报永久代内存溢出的错误。&lt;/p&gt;
&lt;p&gt;在Java 8及之后，方法区被实现在元空间中，不再使用堆内存，转向使用本地内存，元空间可以通过参数设置最大大小，但也会受物理内存的限制。&lt;/p&gt;
&lt;p&gt;所以，如果加载的类过多或者大量动态生成类还是会报元空间内存溢出的错误。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring-Boot-2.x和1.x版本相比有哪些区别与改进</title>
        <link>https://iamxurulin.github.io/p/spring-boot-2.x%E5%92%8C1.x%E7%89%88%E6%9C%AC%E7%9B%B8%E6%AF%94%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8C%BA%E5%88%AB%E4%B8%8E%E6%94%B9%E8%BF%9B/</link>
        <pubDate>Fri, 02 Jan 2026 15:02:23 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot-2.x%E5%92%8C1.x%E7%89%88%E6%9C%AC%E7%9B%B8%E6%AF%94%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8C%BA%E5%88%AB%E4%B8%8E%E6%94%B9%E8%BF%9B/</guid>
        <description>&lt;p&gt;昨天说了&lt;a class=&#34;link&#34; href=&#34;https://blog.csdn.net/qq_44678890/article/details/156487266?spm=1001.2014.3001.5502&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Spring Boot 3.x和2.x版本相比有哪些区别与改进？&lt;/a&gt;，今天来看看Spring Boot 2.x和1.x版本相比有哪些区别与改进？&lt;/p&gt;
&lt;p&gt;1.Spring Boot 1.x基于Spring Framework 4.x，不支持响应式编程；Spring Boot 2.x基于Spring Boot 5，引入了对&lt;strong&gt;响应式编程&lt;/strong&gt;的支持。&lt;/p&gt;
&lt;p&gt;2.Spring Boot 2.x对Tomcat、Jetty这些嵌入式Web容器的默认版本进行了&lt;strong&gt;升级&lt;/strong&gt;，带来了对新HTTP标准的支持。&lt;/p&gt;
&lt;p&gt;3.Spring Boot 2.x对底层组件和框架本身做了大量的&lt;strong&gt;性能优化&lt;/strong&gt;，使得应用启动时间更短，运行性能更高，更加适合云原生应用和大规模的微服务架构。&lt;/p&gt;
&lt;p&gt;4.Spring Boot 1.x中，&lt;strong&gt;Actuator端点&lt;/strong&gt;是默认全部开启的，存在安全上的隐患，Spring Boot 2.x对Actuator进行了全面改进，在默认情况下，大多数端点是关闭的，开发者可以通过配置显示地启动需要的端点。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说编译执行和解释执行的区别</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E7%BC%96%E8%AF%91%E6%89%A7%E8%A1%8C%E5%92%8C%E8%A7%A3%E9%87%8A%E6%89%A7%E8%A1%8C%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Thu, 01 Jan 2026 17:35:59 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E7%BC%96%E8%AF%91%E6%89%A7%E8%A1%8C%E5%92%8C%E8%A7%A3%E9%87%8A%E6%89%A7%E8%A1%8C%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;编译执行是程序在执行之前，先通过编译器将源代码编译为机器代码，然后直接在CPU上运行；&lt;/p&gt;
&lt;p&gt;解释执行是源代码在不经过编译器编译的前提下，直接在运行的时候通过解释器逐行翻译并执行。&lt;/p&gt;
&lt;p&gt;常见的编译性语言有C和C++，而常见的解释性语言有Python。&lt;/p&gt;
&lt;p&gt;编译执行的语言因为编译后的程序不需要在运行的时候再进行翻译，所以运行速度快。&lt;/p&gt;
&lt;p&gt;但是，程序需要针对每个平台重新编译，跨平台性会更差一点。&lt;/p&gt;
&lt;p&gt;而解释执行的语言在每个平台上都是通过相应平台的解释器来运行的，跨平台性好。但是每次执行的时候都需要进行动态的翻译和解释，所以运行速度更慢。&lt;/p&gt;
&lt;p&gt;严格来说，JVM是结合了编译执行和解释执行的。&lt;/p&gt;
&lt;p&gt;正常情况下JVM是解释执行的，不过，如果JVM发现某段逻辑执行的特别频繁，那么它就会通过JIT（Just In Time）即时编译将其编译成机器码，这样就是编译执行了。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java是怎么实现跨平台的</title>
        <link>https://iamxurulin.github.io/p/java%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%9A%84/</link>
        <pubDate>Thu, 01 Jan 2026 17:02:43 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E6%98%AF%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%9A%84/</guid>
        <description>&lt;p&gt;跨平台指的是在不同的硬件或者操作系统上，Java代码在不需要针对不同的平台做对应的修改的前提下，都可以正常运行。&lt;/p&gt;
&lt;p&gt;要实现这样一种&lt;strong&gt;一次编写到处运行&lt;/strong&gt;的特性，主要靠的是JVM，即Java虚拟机。&lt;/p&gt;
&lt;p&gt;和其他编程语言在编译后直接生成特定于某一操作系统的二进制01机器代码不同，Java程序在编译之后生成的是.class格式的字节码。&lt;/p&gt;
&lt;p&gt;JVM为了屏蔽不同硬件或操作系统的底层细节，针对不同的平台做了对应的开发，可以实现将字节码翻译成特定平台上的机器代码并成功执行，这就使得同一份Java字节码可以在任意的支持JVM的平台上正常运行。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring-Boot-3.x和2.x版本相比有哪些区别与改进</title>
        <link>https://iamxurulin.github.io/p/spring-boot-3.x%E5%92%8C2.x%E7%89%88%E6%9C%AC%E7%9B%B8%E6%AF%94%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8C%BA%E5%88%AB%E4%B8%8E%E6%94%B9%E8%BF%9B/</link>
        <pubDate>Thu, 01 Jan 2026 16:05:33 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot-3.x%E5%92%8C2.x%E7%89%88%E6%9C%AC%E7%9B%B8%E6%AF%94%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8C%BA%E5%88%AB%E4%B8%8E%E6%94%B9%E8%BF%9B/</guid>
        <description>&lt;p&gt;1.Spring Boot 2.x基于Java EE，而Spring Boot 3.x迁移到了Jakartaa EE，一些核心的包名也从&lt;code&gt;javax.*&lt;/code&gt;变更为&lt;code&gt;jakarta.*&lt;/code&gt;了。&lt;/p&gt;
&lt;p&gt;2.Spring Boot 2.x支持JDK8、11和17版本，而Spring Boot 3.x要求JDK版本最低为17。&lt;/p&gt;
&lt;p&gt;3.Spring Boot 2.x 没有原生编译的内置支持，Spring Boot3.x则提供了对GraalVM Native Image的开箱即用支持，可以将Spring应用编译成本地的可执行文件。&lt;/p&gt;
&lt;p&gt;4.Spring Boot 2.x仅支持基本的监控和追踪，Spring Boot 3.x引入了更完善的分布式追踪、日志关联和性能指标收集，支持OpenTelemetry标准，开发者可以借助Observability更好地监控和分析应用的运行状况。&lt;/p&gt;
&lt;p&gt;5.Spring Boot 2.x支持Spring Security 5，在安全性上存在一定的局限性；Spring Boot 3.x增强了对Spring Security 6的支持，强化了身份认证、授权和安全配置的能力。&lt;/p&gt;
&lt;p&gt;6.Spring Boot 2.x的依赖库和自动配置很强大，但是存在一些历史遗留的依赖和配置；Spring Boot 3.x对内部依赖进行了一些模块化的调整，对一些不再使用或者过时的库进行了清理。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么时候@Async会失效</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99@async%E4%BC%9A%E5%A4%B1%E6%95%88/</link>
        <pubDate>Thu, 01 Jan 2026 14:12:06 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99@async%E4%BC%9A%E5%A4%B1%E6%95%88/</guid>
        <description>&lt;p&gt;有多种原因可造成@Async失效：&lt;/p&gt;
&lt;p&gt;1.@Async依赖于Spring AOP，如果是内部调用的话则会绕过代理对象，直接调用原始方法。&lt;/p&gt;
&lt;p&gt;2.Spring AOP默认只会对public方法生效，对于非public方法则不会被代理，所以此时@Async是失效的。&lt;/p&gt;
&lt;p&gt;3.如果Spring Boot的主类或者配置类上没有添加@EnableAsync注解，也就是没有显示启用异步功能，则Spring是不会为其生成代理的。&lt;/p&gt;
&lt;p&gt;4.@Async依赖于Spring容器管理的Bean，如果是手动new的对象，并没有被Spring管理，则代理机制也是会失效的。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说一说Java中堆和栈的区别</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E8%AF%B4java%E4%B8%AD%E5%A0%86%E5%92%8C%E6%A0%88%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Wed, 31 Dec 2025 16:04:55 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E8%AF%B4java%E4%B8%AD%E5%A0%86%E5%92%8C%E6%A0%88%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;栈主要用于存储&lt;strong&gt;局部变量&lt;/strong&gt;和&lt;strong&gt;方法的返回地址、参数&lt;/strong&gt;等调用信息。&lt;/p&gt;
&lt;p&gt;堆主要用于存储对象实例和数组。&lt;/p&gt;
&lt;p&gt;JVM的垃圾回收主要是针对堆空间的一个处理，而栈空间是不会被回收的。&lt;/p&gt;
&lt;p&gt;在一次方法的调用过程中，调用的时候数据存入栈空间，当方法执行完之后，数据就被弹出释放，&lt;/p&gt;
&lt;p&gt;不需要像堆空间一样等待GC进行回收，所以相对而言，栈空间的生命周期会比堆空间更短。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Spring-Boot工程启动以后-怎么将数据库中已有的固定内容打入到Redis缓存中</title>
        <link>https://iamxurulin.github.io/p/spring-boot%E5%B7%A5%E7%A8%8B%E5%90%AF%E5%8A%A8%E4%BB%A5%E5%90%8E-%E6%80%8E%E4%B9%88%E5%B0%86%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E5%B7%B2%E6%9C%89%E7%9A%84%E5%9B%BA%E5%AE%9A%E5%86%85%E5%AE%B9%E6%89%93%E5%85%A5%E5%88%B0redis%E7%BC%93%E5%AD%98%E4%B8%AD/</link>
        <pubDate>Wed, 31 Dec 2025 15:49:46 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/spring-boot%E5%B7%A5%E7%A8%8B%E5%90%AF%E5%8A%A8%E4%BB%A5%E5%90%8E-%E6%80%8E%E4%B9%88%E5%B0%86%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E5%B7%B2%E6%9C%89%E7%9A%84%E5%9B%BA%E5%AE%9A%E5%86%85%E5%AE%B9%E6%89%93%E5%85%A5%E5%88%B0redis%E7%BC%93%E5%AD%98%E4%B8%AD/</guid>
        <description>&lt;p&gt;要想实现在Spring Boot工程启动之后，直接将数据库中已有的固定内容打入到Redis缓存中，首先需要确保实体类已经实现了Serializable接口，并且已经正确配置了RedisTemplate的序列化方式 。&lt;/p&gt;
&lt;p&gt;可以考虑使用CommanLineRunner或者ApplicationRunner&lt;strong&gt;接口&lt;/strong&gt;，在应用启动完成后执行初始化逻辑；&lt;/p&gt;
&lt;p&gt;也可以通过@PostConstruct注解，在Bean创建之后立即执行数据库加载。&lt;/p&gt;
&lt;p&gt;在懒加载场景，可以使用@Cacheable注解，在首次调用方法时触发缓存写入，完成预加载。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Linux系统中crontab的工作原理</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4linux%E7%B3%BB%E7%BB%9F%E4%B8%ADcrontab%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/</link>
        <pubDate>Tue, 30 Dec 2025 15:07:15 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4linux%E7%B3%BB%E7%BB%9F%E4%B8%ADcrontab%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86/</guid>
        <description>&lt;p&gt;crontab是Linux系统中的一个&lt;strong&gt;定时任务调度器&lt;/strong&gt;，能够允许用户在一个特定的时间执行某些任务。&lt;/p&gt;
&lt;p&gt;crontab指令&lt;strong&gt;通常&lt;/strong&gt;是由5个时间字段和1个命令字段组成：&lt;/p&gt;
&lt;p&gt;5个时间字段分别是：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;分钟&lt;/th&gt;
          &lt;th&gt;0~59&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;小时&lt;/td&gt;
          &lt;td&gt;0~23&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;日期&lt;/td&gt;
          &lt;td&gt;1~31&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;月份&lt;/td&gt;
          &lt;td&gt;1~12&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;星期几&lt;/td&gt;
          &lt;td&gt;0~7&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;其中，0和7都表示星期日。&lt;/p&gt;
&lt;p&gt;如果想要在凌晨2点执行一个脚本，可以执行如下crontab指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; * * * /path/to/script.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>说一说Linux系统中进程和线程的区别</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E8%AF%B4linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Tue, 30 Dec 2025 14:50:54 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E8%AF%B4linux%E7%B3%BB%E7%BB%9F%E4%B8%AD%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;我们先明确定义：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程&lt;/strong&gt;是一个具有独立运行环境的程序实例，操作系统可以通过进程调度来管理CPU的时间分配。&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;线程&lt;/strong&gt;则是进程中一个单独的执行路径，并且一个进程中可以包含多个线程，这些线程共享进程的地址空间和其他资源。&lt;/p&gt;
&lt;p&gt;主要区别在于：&lt;/p&gt;
&lt;p&gt;1.进程是资源分配的基本单位，线程是CPU调度的基本单位。&lt;/p&gt;
&lt;p&gt;2.进程与进程之间是相互独立的，也就是说一个进程的崩溃并不会对其他的进程造成影响。但是，线程之间因为是共享进程的资源的，所以，如果一个线程崩溃了，可能会导致整个进程的崩溃。&lt;/p&gt;
&lt;p&gt;3.进程之间的切换开销是大于线程之间的切换的，因为线程是在同一进城内切换，所以会相对轻松点。&lt;/p&gt;
&lt;p&gt;4.进程主要应用于Web服务器和数据库管理系统这些场景，线程则主要应用于多线程下载器和并发处理计算任务的场景，比如Java线程池等等。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Linux中如何查看系统中内存、CPU和网络端口的使用情况</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8linux%E4%B8%AD%E5%A6%82%E4%BD%95%E6%9F%A5%E7%9C%8B%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%86%85%E5%AD%98cpu%E5%92%8C%E7%BD%91%E7%BB%9C%E7%AB%AF%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</link>
        <pubDate>Tue, 30 Dec 2025 14:29:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8linux%E4%B8%AD%E5%A6%82%E4%BD%95%E6%9F%A5%E7%9C%8B%E7%B3%BB%E7%BB%9F%E4%B8%AD%E5%86%85%E5%AD%98cpu%E5%92%8C%E7%BD%91%E7%BB%9C%E7%AB%AF%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8%E6%83%85%E5%86%B5/</guid>
        <description>&lt;h4 id=&#34;1top命令&#34;&gt;1.top命令
&lt;/h4&gt;&lt;p&gt;top命令可以实时地查看当前系统CPU使用率、内存使用率、进场ID等信息。&lt;/p&gt;
&lt;h4 id=&#34;2free命令&#34;&gt;2.free命令
&lt;/h4&gt;&lt;p&gt;free命令可以查看系统中包括总内存量、已使用内存量和可用内存量等内存使用情况。&lt;/p&gt;
&lt;h4 id=&#34;3df命令&#34;&gt;3.df命令
&lt;/h4&gt;&lt;p&gt;df命令可以查看系统中包括磁盘总容量、已使用空间和可用空间等磁盘使用情况。&lt;/p&gt;
&lt;h4 id=&#34;4iostat命令&#34;&gt;4.iostat命令
&lt;/h4&gt;&lt;p&gt;iostat命令可以查看当前系统的CPU和I/O使用情况。&lt;/p&gt;
&lt;h4 id=&#34;5netstat命令&#34;&gt;5.netstat命令
&lt;/h4&gt;&lt;p&gt;netstat命令可以查看系统中像本地地址、远程地址、连接状态等网络连接情况。&lt;/p&gt;
&lt;h4 id=&#34;6lsof命令&#34;&gt;6.lsof命令
&lt;/h4&gt;&lt;p&gt;lsof命令可以查看当前系统中打开的文件和网络连接情况，像文件名、文件扫描符、进程ID、进程名等等。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说CC攻击、DDOS攻击和网站数据库注入</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4cc%E6%94%BB%E5%87%BBddos%E6%94%BB%E5%87%BB%E5%92%8C%E7%BD%91%E7%AB%99%E6%95%B0%E6%8D%AE%E5%BA%93%E6%B3%A8%E5%85%A5/</link>
        <pubDate>Tue, 30 Dec 2025 14:15:03 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4cc%E6%94%BB%E5%87%BBddos%E6%94%BB%E5%87%BB%E5%92%8C%E7%BD%91%E7%AB%99%E6%95%B0%E6%8D%AE%E5%BA%93%E6%B3%A8%E5%85%A5/</guid>
        <description>&lt;p&gt;CC攻击和DDOS攻击都是一种&lt;strong&gt;网络攻击方式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CC 攻击&lt;/strong&gt;是攻击者使用大量的机器或者网络中的代理服务器，向某个目标服务器发送大量的请求，以消耗服务器的带宽和资源，导致服务器瘫痪。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DDOS攻击&lt;/strong&gt;（Distributed Denial of Service Attack）也是利用大量的计算机或者网络中的代理服务器，&lt;strong&gt;同时&lt;/strong&gt;向某个目标服务器发送大量的请求，导致服务器瘫痪。&lt;/p&gt;
&lt;p&gt;CC攻击其实是DDOS攻击的一种特定的形式，专门针对Web应用程序的一种攻击。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;网站数据库注入&lt;/strong&gt;则是一种&lt;strong&gt;利用Web应用程序漏洞&lt;/strong&gt;的攻击方式。&lt;/p&gt;
&lt;p&gt;这种攻击方式，攻击者通过将恶意SQL代码插入到Web应用程序的输入字段，对数据库进行未授权访问，从而破坏数据库的完整性、泄露敏感数据等等。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>IDEA2023中新建Spring-Boot2.X版本的工程的方法</title>
        <link>https://iamxurulin.github.io/p/idea2023%E4%B8%AD%E6%96%B0%E5%BB%BAspring-boot2.x%E7%89%88%E6%9C%AC%E7%9A%84%E5%B7%A5%E7%A8%8B%E7%9A%84%E6%96%B9%E6%B3%95/</link>
        <pubDate>Tue, 30 Dec 2025 09:40:44 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/idea2023%E4%B8%AD%E6%96%B0%E5%BB%BAspring-boot2.x%E7%89%88%E6%9C%AC%E7%9A%84%E5%B7%A5%E7%A8%8B%E7%9A%84%E6%96%B9%E6%B3%95/</guid>
        <description>&lt;p&gt;因为官方 Spring Initializr 服务（start.spring.io）已停止对 Spring Boot 2.x 系列旧版本的支持，仅保留 3.5.0 及以上新版本的分发，因此无法通过该服务获取 2.x 版本的项目模板。
&lt;img src=&#34;https://iamxurulin.github.io/images/dcfa7a57624b4c05af96659f073adc81-9839bdc2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;并且Spring Boot 3.x 强制要求 Java ≥17，只有 2.x 系列支持 Java 8，所以会导致下拉框选不到Java8。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/7cf215e5acee4eabb045bf5323b76c0f-b441c231.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
为了解决这个问题，需要将在IDEA 的 Spring Initializr 界面，点击顶部「Server URL」输入框，删除原有地址，输入阿里云的服务地址：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://start.aliyun.com
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;之后就能够选择Java 8了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/1417c26821a54423b00709d290e1de38-6e9dc066.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
点击Next之后，也能够选择2.X 系列的Spring Boot版本。
&lt;img src=&#34;https://iamxurulin.github.io/images/694c23ce00a0423cb6d399832a0d73ba-538946dc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Linux系统的硬链接和软链接有甚区别</title>
        <link>https://iamxurulin.github.io/p/linux%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%A1%AC%E9%93%BE%E6%8E%A5%E5%92%8C%E8%BD%AF%E9%93%BE%E6%8E%A5%E6%9C%89%E7%94%9A%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Mon, 29 Dec 2025 17:04:55 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/linux%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%A1%AC%E9%93%BE%E6%8E%A5%E5%92%8C%E8%BD%AF%E9%93%BE%E6%8E%A5%E6%9C%89%E7%94%9A%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;在Linux系统中，硬链接和软链接都是用于将一个文件链接到另一个文件。&lt;/p&gt;
&lt;p&gt;主要区别如下：&lt;/p&gt;
&lt;p&gt;1.硬链接是通过在文件系统中创建一个&lt;strong&gt;新的目录项&lt;/strong&gt;指向同一文件的inode的位置来实现的，而软链接是通过在文件系统中创建一个包含指向另一个文件的路径的&lt;strong&gt;新文件&lt;/strong&gt;来实现。&lt;/p&gt;
&lt;p&gt;2.硬链接只能指向同一个文件系统内的文件，并不能&lt;strong&gt;跨文件系统&lt;/strong&gt;创建，而软链接可以跨文件系统创建，并且可以指向任意类型的文件。&lt;/p&gt;
&lt;p&gt;3.因为硬链接实际上指向的是同一inode，所以如果&lt;strong&gt;原文件被删除了&lt;/strong&gt;，硬链接还是能够访问到原文件的内容；但是对于软链接来说，如果原文件被删除了，软链接也会跟着失效。&lt;/p&gt;
&lt;p&gt;4.因为硬链接只是增加了一个新的目录项，所以对&lt;strong&gt;磁盘空间的消耗&lt;/strong&gt;比软链接要更小。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么是循环依赖</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AF%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96/</link>
        <pubDate>Mon, 29 Dec 2025 16:48:25 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AF%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96/</guid>
        <description>&lt;p&gt;循环依赖是指两个或者多个bean之间相互引用，形成了一个闭环。&lt;/p&gt;
&lt;p&gt;最典型的场景就是：&lt;/p&gt;
&lt;p&gt;比如Spring正在创建Bean A，发现它依赖B，于是就去创建B，结果在创建B的时候，又发现它依赖A。&lt;/p&gt;
&lt;p&gt;但是这个时候A正在创建中，还没有完全生成，这样B就拿不到A的引用，所以该咋办呢？&lt;/p&gt;
&lt;p&gt;这里又分为&lt;strong&gt;构造器注入&lt;/strong&gt;和&lt;strong&gt;Setter注入&lt;/strong&gt;两种情况：&lt;/p&gt;
&lt;p&gt;如果是构造器注入，那好办，Spring直接就给你抛出&lt;strong&gt;BeanCurrentlyInCreationException&lt;/strong&gt;错误。&lt;/p&gt;
&lt;p&gt;如果是Setter注入，Spring则是通过&lt;strong&gt;三级缓存机制&lt;/strong&gt;来解决。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Kafka的索引设计有什么亮点</title>
        <link>https://iamxurulin.github.io/p/kafka%E7%9A%84%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E6%9C%89%E4%BB%80%E4%B9%88%E4%BA%AE%E7%82%B9/</link>
        <pubDate>Mon, 29 Dec 2025 16:31:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/kafka%E7%9A%84%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E6%9C%89%E4%BB%80%E4%B9%88%E4%BA%AE%E7%82%B9/</guid>
        <description>&lt;p&gt;Kafka索引设计的亮点主要有以下4个方面：&lt;/p&gt;
&lt;p&gt;1.kafka通过&lt;strong&gt;稀疏索引&lt;/strong&gt;只存储每隔一定间隔的消息位置，而不是对每条消息都建立索引，这样可以大幅度减少内存的占用。&lt;/p&gt;
&lt;p&gt;2.Kafka通过将日志文件拆分成多个&lt;strong&gt;段文件&lt;/strong&gt;来存储，每个段文件中包含一个日志文件和对应的索引文件。&lt;/p&gt;
&lt;p&gt;3.kafka为了降低磁盘的随机写入成本，采用&lt;strong&gt;顺序写入&lt;/strong&gt;文件的方式，提升了写入性能。&lt;/p&gt;
&lt;p&gt;4.Kafka为了避免逐个扫描，通过&lt;strong&gt;offset&lt;/strong&gt;定位消息，再结合稀疏索引可以快速定位到近似位置后再进行顺序查找，从而保证高效地读取。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>kafka中的时间轮实现</title>
        <link>https://iamxurulin.github.io/p/kafka%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E8%BD%AE%E5%AE%9E%E7%8E%B0/</link>
        <pubDate>Mon, 29 Dec 2025 15:49:00 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/kafka%E4%B8%AD%E7%9A%84%E6%97%B6%E9%97%B4%E8%BD%AE%E5%AE%9E%E7%8E%B0/</guid>
        <description>&lt;p&gt;Kafka中的时间轮是一种用于实现&lt;strong&gt;定时清理&lt;/strong&gt;、&lt;strong&gt;消息过期&lt;/strong&gt;、&lt;strong&gt;心跳超时&lt;/strong&gt;等功能的定时任务调度机制。&lt;/p&gt;
&lt;p&gt;通过时间轮可以避免在大量的定时任务中进行逐个扫描，可以实现接近&lt;strong&gt;O(1)&lt;strong&gt;的延迟任务管理，降低了内存和CPU的消耗，比较适合管理大量&lt;/strong&gt;短时和中长期的定时任务&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;时间轮，它可以看成是一个&lt;strong&gt;环形数组&lt;/strong&gt;，其中，每个槽表示一个时间间隔。&lt;/p&gt;
&lt;p&gt;Kafka会在时间轮的每个槽中根据&lt;strong&gt;任务的到期时间&lt;/strong&gt;放置延迟任务，当时间轮转到任务所在的槽时，就执行该槽内的任务。&lt;/p&gt;
&lt;p&gt;如果存在到期未完成的任务，则会被&lt;strong&gt;重新分配到下一轮&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Kafka中关于事务消息的实现</title>
        <link>https://iamxurulin.github.io/p/kafka%E4%B8%AD%E5%85%B3%E4%BA%8E%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
        <pubDate>Mon, 29 Dec 2025 15:06:48 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/kafka%E4%B8%AD%E5%85%B3%E4%BA%8E%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid>
        <description>&lt;p&gt;Kafka的事务消息实现了消息在生产、传输和消费的过程中的“仅一次”传递，也就是所谓的Exactly Once语义。&lt;/p&gt;
&lt;p&gt;Kafka的事务消息主要由&lt;strong&gt;事务协调器&lt;/strong&gt;、&lt;strong&gt;幂等生产者&lt;/strong&gt;和&lt;strong&gt;事务性消费&lt;/strong&gt;这三个核心组件来实现。&lt;/p&gt;
&lt;p&gt;事务协调器主要负责事务的启动、提交和终止管理，将事务状态记录到__transaction_state内部的主题；&lt;/p&gt;
&lt;p&gt;为了确保同一事务的每条消息只写入一次，Kafka Producer通过Producer ID来作为事务的唯一性标志。&lt;/p&gt;
&lt;p&gt;在消费的过程中，消费者可以选择只消费已提交的事务消息，从而确保数据的最终一致性。&lt;/p&gt;
&lt;p&gt;我们可以理一下Kafka的事务消息流程：&lt;/p&gt;
&lt;p&gt;首先是生产者向Transaction Coordinator&lt;strong&gt;请求&lt;/strong&gt;启动事务；&lt;/p&gt;
&lt;p&gt;接下来生产者为了保证幂等性，给每条消息都带上唯一的Producer ID和Sequence Number，&lt;strong&gt;开始向Kafka写入事务消息&lt;/strong&gt;；&lt;/p&gt;
&lt;p&gt;当所有消息都写入完成后，生产者开始向事务协调去发送commit或者abort请求提交或者终止事务。&lt;/p&gt;
&lt;p&gt;最后，消费者为了实现最终的数据一致性，可以通过设置read_commited 隔离级别，&lt;strong&gt;只消费已提交的消息&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>RocketMQ的事务消息有什么缺点你知道吗</title>
        <link>https://iamxurulin.github.io/p/rocketmq%E7%9A%84%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E6%9C%89%E4%BB%80%E4%B9%88%E7%BC%BA%E7%82%B9%E4%BD%A0%E7%9F%A5%E9%81%93%E5%90%97/</link>
        <pubDate>Sun, 28 Dec 2025 17:01:10 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/rocketmq%E7%9A%84%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E6%9C%89%E4%BB%80%E4%B9%88%E7%BC%BA%E7%82%B9%E4%BD%A0%E7%9F%A5%E9%81%93%E5%90%97/</guid>
        <description>&lt;p&gt;RockMQ事务消息的缺点主要就以下几个方面：&lt;/p&gt;
&lt;p&gt;从&lt;strong&gt;改造成本&lt;/strong&gt;来看，RocketMQ需要改造它的原始逻辑来实现一个特定的接口，并且还需要在应用层来处理一个复杂的回查逻辑，从而确保回查不会重复或者丢失。&lt;/p&gt;
&lt;p&gt;在&lt;strong&gt;可用性&lt;/strong&gt;方面，由于RocketMQ的事务消息的实现是先发送半消息，如果MQ集群挂了，那么半消息就没法发送成功，后续的逻辑就无法再执行下去了，也就是说整个应用无法正常执行了。&lt;/p&gt;
&lt;p&gt;还有一个缺点就是RocketMQ只支持&lt;strong&gt;单事务消息&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说消息队列的推模式和拉模式</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E7%9A%84%E6%8E%A8%E6%A8%A1%E5%BC%8F%E5%92%8C%E6%8B%89%E6%A8%A1%E5%BC%8F/</link>
        <pubDate>Sun, 28 Dec 2025 16:28:14 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E7%9A%84%E6%8E%A8%E6%A8%A1%E5%BC%8F%E5%92%8C%E6%8B%89%E6%A8%A1%E5%BC%8F/</guid>
        <description>&lt;p&gt;&lt;strong&gt;推模式&lt;/strong&gt;是消息队列主动将消息从Broker推送给Consumer，这种模式它的实时性比较好，消息可以立即送达消费者，但是缺点就是决定权不在消费者手上，在高并发场景下容易造成消费者过载。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拉模式&lt;/strong&gt;是消费者主动从消息队列中的Broker拉取消息，这种模式就是根据消费者自身的负载和消费能力来决定拉去消息的频率，这样就可以避免过载，但是这种模式的实时性比不上推模式，而且还会导致消息延迟。&lt;/p&gt;
&lt;p&gt;RocketMQ和Kafka都选择了&lt;strong&gt;拉模式&lt;/strong&gt;，对于拉模式无法保证实时性的缺点，他们采取的是&lt;strong&gt;长轮询&lt;/strong&gt;的方式来解决。&lt;/p&gt;
&lt;p&gt;主要是通过在消费者去Broker拉取消息时，当有消息存在的话，那自然就直接返回消息；当没有消息时，则保持连接，暂时hold住请求，之后如果在对应的队列或者分区有新的消息到来时就通过之前hold住的请求及时地返回消息。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>消息队列如何处理消息堆积</title>
        <link>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E6%B6%88%E6%81%AF%E5%A0%86%E7%A7%AF/</link>
        <pubDate>Sun, 28 Dec 2025 15:14:44 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E6%B6%88%E6%81%AF%E5%A0%86%E7%A7%AF/</guid>
        <description>&lt;p&gt;在消息队列中，当消息的生产速度远远大于消费速度时，将会导致大量的消息积压在队列中，这样就会形成&lt;strong&gt;消息堆积&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;那如何处理消息堆积呢？&lt;/p&gt;
&lt;p&gt;一般来说，我们需要定位一下消费慢的原因，如果是bug则处理bug；
如果是因为消费者自身的&lt;strong&gt;消费能力弱&lt;/strong&gt;，则需要考虑提升消费者的消费能力。&lt;/p&gt;
&lt;p&gt;那怎么提高消费者的消费能力呢？&lt;/p&gt;
&lt;p&gt;1.可以考虑增加消费者的线程数量，提高消费者的并发消费能力；&lt;/p&gt;
&lt;p&gt;2.如果是在分布式系统中，可以增加多个消费实例，从而提高消费速率。&lt;/p&gt;
&lt;p&gt;3.优化消费者的代码，比如可以减少I/O操作或者使用批量处理等等。&lt;/p&gt;
&lt;p&gt;上面这些是通过消费者端进行解决的方法，如果是从生产端解决的话，可以考虑对生产端进行&lt;strong&gt;限流&lt;/strong&gt;，降低生产速率。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>消息队列如何保证消息的有效性</title>
        <link>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E6%B6%88%E6%81%AF%E7%9A%84%E6%9C%89%E6%95%88%E6%80%A7/</link>
        <pubDate>Sat, 27 Dec 2025 17:56:38 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E6%B6%88%E6%81%AF%E7%9A%84%E6%9C%89%E6%95%88%E6%80%A7/</guid>
        <description>&lt;p&gt;为了保证消息的有效性，可采取如下方法：&lt;/p&gt;
&lt;p&gt;1.为了确保消息按照生产者的发送顺序来消费，可以使用&lt;strong&gt;单个生产者&lt;/strong&gt;向&lt;strong&gt;单个队列&lt;/strong&gt;发送消息，再由&lt;strong&gt;单个消费者&lt;/strong&gt;来处理消息。&lt;/p&gt;
&lt;p&gt;2.对于像Kafka和RocketMQ这种支持分区的消息队列，可以通过&lt;strong&gt;Partition Key&lt;/strong&gt;将消息发送到一个特定的分区。因为每个分区的内部都是有序的，这样一来就能保证具有相同Partition Key的消息都按照顺序来消费。&lt;/p&gt;
&lt;p&gt;3.对于像RabbitMQ这种支持&lt;strong&gt;顺序队列&lt;/strong&gt;的消息队列，因为消息在队列中的存储顺序和投递顺序都是一致的，如果使用单个顺序队列，那么消息也将按照顺序被消费。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>消息队列如何处理重复消息</title>
        <link>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E9%87%8D%E5%A4%8D%E6%B6%88%E6%81%AF/</link>
        <pubDate>Sat, 27 Dec 2025 17:03:04 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%E9%87%8D%E5%A4%8D%E6%B6%88%E6%81%AF/</guid>
        <description>&lt;p&gt;对于平常的业务而言，消息重复一般是不可避免的，我们只能在业务上来处理重复消息所带来的影响。&lt;/p&gt;
&lt;p&gt;为了保证同一条消息无论被消费多少次，它的结果都是一样的，需要让消费者的处理逻辑具有幂等性。&lt;/p&gt;
&lt;p&gt;为了实现幂等处理重复消息，可以给每条消息分配一个全局的唯一ID，当消费者接收消息时，先检查一下这个ID是否已经处理，如果已经处理则直接返回，如果没有处理，则执行逻辑并记录一下ID。&lt;/p&gt;
&lt;p&gt;除了分配全局唯一ID，对于订单数据来说，还可以设计一个严格的状态流转（比如：待支付→已支付→待发货→已发货），这样在每次处理消息前对当前状态进行检查，看是否符合操作条件。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>消息队列如何保证消息不丢失</title>
        <link>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E6%B6%88%E6%81%AF%E4%B8%8D%E4%B8%A2%E5%A4%B1/</link>
        <pubDate>Sat, 27 Dec 2025 16:15:36 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E6%B6%88%E6%81%AF%E4%B8%8D%E4%B8%A2%E5%A4%B1/</guid>
        <description>&lt;p&gt;为了保证消息不丢失，需要&lt;strong&gt;生产消&lt;/strong&gt;息、&lt;strong&gt;存储消息&lt;/strong&gt;和&lt;strong&gt;消费消息&lt;/strong&gt;这三个阶段协同配合。&lt;/p&gt;
&lt;p&gt;当生产者在发送消息时，需要通过消息确认机制来确保消息的成功到达。&lt;/p&gt;
&lt;p&gt;我们知道存储在内存中的消息是断电不保存的，因此，为了避免消息因为服务器的宕机或者重启而丢失，需要在Broker收到消息后，将其持久化到磁盘中。&lt;/p&gt;
&lt;p&gt;在消费者处理完消息后，需要向消息队列发送确认，如果没有收到消费者发送过来的确认，消息队列则需要重新投递该消息。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说一下消息队列有哪些模型</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E4%B8%8B%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E6%9C%89%E5%93%AA%E4%BA%9B%E6%A8%A1%E5%9E%8B/</link>
        <pubDate>Sat, 27 Dec 2025 15:36:30 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E4%B8%80%E4%B8%8B%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E6%9C%89%E5%93%AA%E4%BA%9B%E6%A8%A1%E5%9E%8B/</guid>
        <description>&lt;p&gt;常见的消息队列模型主要有&lt;strong&gt;发布/订阅模型&lt;/strong&gt;和&lt;strong&gt;队列模型&lt;/strong&gt;（也称点对点模型）两种。&lt;/p&gt;
&lt;p&gt;那什么是队列模型呢？所谓的队列模型，指的是消息从生产者发送到队列中，其中的消息只能被一个消费者消费一次，在消费者消费完之后，消息就在队列中被删除了。&lt;/p&gt;
&lt;p&gt;而发布/订阅模型，则指的是生产者将消息发布到某个Topic中，这样所有订阅了这个主题的消费者都可以接收到这个消息，这种模型比较适用于像广播通知和实时推送这样的场景。&lt;/p&gt;
&lt;p&gt;需要说明的是，RabbitMQ虽然具有发布/订阅模式，但是在本质上RabbitMQ还是通过同时将消息发送给多个队列来模拟出发布/订阅的效果，其底层依然是基于队列模型的。&lt;/p&gt;
&lt;p&gt;而RocketMQ和Kafka则都是采用发布/订阅模型的。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>解释一下什么是消息队列</title>
        <link>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8B%E4%BB%80%E4%B9%88%E6%98%AF%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/</link>
        <pubDate>Sat, 27 Dec 2025 14:42:56 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8B%E4%BB%80%E4%B9%88%E6%98%AF%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/</guid>
        <description>&lt;p&gt;消息队列是一种用于在分布式系统中，解耦发送方和接收方之间通信的&lt;strong&gt;异步通信机制&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;消息队列通过引入一个broker作为中间缓冲区，然后将消息存储在broker中，接下来再由消费者从broker中读取和处理消息，从而实现异步通信。&lt;/p&gt;
&lt;p&gt;消息队列常见的用途主要有：&lt;/p&gt;
&lt;p&gt;1.生产者可以在发送消息之后立即返回，而消费者也可以在恰当的时机对消息进行处理。&lt;/p&gt;
&lt;p&gt;2.在高并发的场景下，消息队列可以暂时存储大量的请求，平滑高峰期的流量，不至于使系统过载。&lt;/p&gt;
&lt;p&gt;3.为了减少用户请求的响应时间，可以选择将不需要立即处理的任务放入消息队列中异步执行。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说RocketMQ中关于事务消息的实现</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rocketmq%E4%B8%AD%E5%85%B3%E4%BA%8E%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0/</link>
        <pubDate>Fri, 26 Dec 2025 16:46:06 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4rocketmq%E4%B8%AD%E5%85%B3%E4%BA%8E%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0/</guid>
        <description>&lt;p&gt;在RocketMQ中，为了确保消息与本地事务的&lt;strong&gt;一致性&lt;/strong&gt;，生产者会先将消息发送到RocketMQ的Topic中，这个时候消息的状态为半消息，对于消费者来说，还是不可见的。&lt;/p&gt;
&lt;p&gt;接下来，生产者会执行本地事务：&lt;/p&gt;
&lt;p&gt;如果本地事务&lt;strong&gt;成功&lt;/strong&gt;了，生产者就会向RocketMQ执行commit操作，将半消息转变为正式消息，这样消费者就可见了。&lt;/p&gt;
&lt;p&gt;如果本地事务&lt;strong&gt;失败&lt;/strong&gt;了，生产者就会向RocketMQ执行Rollback操作，然后RocketMQ就会丢弃这个半消息。&lt;/p&gt;
&lt;p&gt;如果生产者没有及时地执行commit或者rollback操作，那么RocketMQ就会定时地回查本地事务的状态，从而来决定是否commit或者rollback消息。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java面向对象编程和面向过程编程的区别是什么</title>
        <link>https://iamxurulin.github.io/p/java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%E5%92%8C%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B%E7%BC%96%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88/</link>
        <pubDate>Fri, 26 Dec 2025 15:11:26 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%E5%92%8C%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B%E7%BC%96%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88/</guid>
        <description>&lt;p&gt;宏观层面上说：&lt;/p&gt;
&lt;p&gt;面向对象是一种以&lt;strong&gt;对象&lt;/strong&gt;为中心，以&lt;strong&gt;人类的思维方式&lt;/strong&gt;来编写代码的编程风格，&lt;/p&gt;
&lt;p&gt;而面向过程则是一种以&lt;strong&gt;过程&lt;/strong&gt;或者说&lt;strong&gt;函数&lt;/strong&gt;为中心，以&lt;strong&gt;计算机的思维方式&lt;/strong&gt;来写代码的编程风格。&lt;/p&gt;
&lt;p&gt;主要有以下几点区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;面向对象主要关注的是对象之间的关系和交互，而面向过程主要关注函数的执行步骤和顺序。&lt;/li&gt;
&lt;li&gt;面向对象的数据和行为都是封装在对象内部的，而面向过程的数据和函数却是分离的。&lt;/li&gt;
&lt;li&gt;面向对象通过它的封装、继承和多态机制 具有较高的复用性和可拓展性，而面向过程如果进行扩展可能需要修改已有的代码，复用性往往较低。&lt;/li&gt;
&lt;li&gt;从适用场景上来说，面向对象很适合一些复杂系统的开发，而面向过程则只适用于一些简单的顺序性较强的小型项目的开发。&lt;/li&gt;
&lt;/ol&gt;
</description>
        </item>
        <item>
        <title>你知道为什么Java不支持多重继承吗</title>
        <link>https://iamxurulin.github.io/p/%E4%BD%A0%E7%9F%A5%E9%81%93%E4%B8%BA%E4%BB%80%E4%B9%88java%E4%B8%8D%E6%94%AF%E6%8C%81%E5%A4%9A%E9%87%8D%E7%BB%A7%E6%89%BF%E5%90%97/</link>
        <pubDate>Fri, 26 Dec 2025 14:20:22 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BD%A0%E7%9F%A5%E9%81%93%E4%B8%BA%E4%BB%80%E4%B9%88java%E4%B8%8D%E6%94%AF%E6%8C%81%E5%A4%9A%E9%87%8D%E7%BB%A7%E6%89%BF%E5%90%97/</guid>
        <description>&lt;p&gt;&lt;img src=&#34;https://iamxurulin.github.io/images/9b14b83fe0e24089b516cdb7a2327178-44c1f192.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
看看这张图，B和C都继承了A，D又继承了B和C。&lt;/p&gt;
&lt;p&gt;想象这样一种场景，如果此时需要调用D中的一个定义在A的方法，但是呢，这个方法在B和C中存在不同的实现，这个时候就会出现歧义，从而无法抉择应该调用哪个。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说Java中的不可变类。</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E4%B8%8D%E5%8F%AF%E5%8F%98%E7%B1%BB/</link>
        <pubDate>Thu, 25 Dec 2025 17:47:54 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4java%E4%B8%AD%E7%9A%84%E4%B8%8D%E5%8F%AF%E5%8F%98%E7%B1%BB/</guid>
        <description>&lt;p&gt;Java中的不可变类指的是在&lt;strong&gt;对象完成创建之后无法被修改的类&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最典型的就是String，当然还有所有的基本类型的包装类，比如Integer、Long、Boolean等等。&lt;/p&gt;
&lt;p&gt;如果要实现一个不可变类，至少需要同时满足以下5个条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;为了防止子类继承父类之后添加了一些可变行为，导致不可变性遭到破坏，需要将类声明为&lt;strong&gt;final&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为了保证字段只能在&lt;strong&gt;构造阶段赋值&lt;/strong&gt;，并且之后就不能通过外部进行访问或者修改，需要通过private final对字段进行修饰，将变量锁死。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;需要通过构造函数一次性初始化所有字段。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以提供getter方法，但是不能提供setter或者修改状态的方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果类中含有可变对象的引用，需要确保在对象外部无法对这些引用进行修改。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样一个不可变类，因为不能对它进行修改，所以多线程访问时不需要加锁，天生的线程安全。&lt;/p&gt;
&lt;p&gt;又因为它的状态不可变，所以很适合作为缓存的key。&lt;/p&gt;
&lt;p&gt;不过，实现这种不可变的代价就是比较费内存。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>在Java中参数传递的方式是按值还是按引用</title>
        <link>https://iamxurulin.github.io/p/%E5%9C%A8java%E4%B8%AD%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E7%9A%84%E6%96%B9%E5%BC%8F%E6%98%AF%E6%8C%89%E5%80%BC%E8%BF%98%E6%98%AF%E6%8C%89%E5%BC%95%E7%94%A8/</link>
        <pubDate>Thu, 25 Dec 2025 16:57:10 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E5%9C%A8java%E4%B8%AD%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E7%9A%84%E6%96%B9%E5%BC%8F%E6%98%AF%E6%8C%89%E5%80%BC%E8%BF%98%E6%98%AF%E6%8C%89%E5%BC%95%E7%94%A8/</guid>
        <description>&lt;p&gt;其实啊，在Java中，不管是&lt;strong&gt;基本数据类型&lt;/strong&gt;还是&lt;strong&gt;引用数据类型&lt;/strong&gt;，参数传递的方式都只有按值传递这一种。&lt;/p&gt;
&lt;p&gt;对于&lt;strong&gt;基本数据类型&lt;/strong&gt;，也就是（ byte, short, int, double, long, float, boolean, char）这8种，这些都是存储在&lt;strong&gt;栈内存&lt;/strong&gt;中的。作为参数传递时，它们传递的都是&lt;strong&gt;值的副本&lt;/strong&gt;，也就是基本数据类型的数值本身。&lt;/p&gt;
&lt;p&gt;对方法中的参数进行操作时，并不会影响原始的变量。&lt;/p&gt;
&lt;p&gt;对于&lt;strong&gt;引用数据类型&lt;/strong&gt;，也就是所有的对象和数组，它们存储的是&lt;strong&gt;对象在堆内存中的地址&lt;/strong&gt;，作为参数传递时，传递的是&lt;strong&gt;引用的副本&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;方法内的修改可以通过引用影响到传入的对象的内容，但是并不会影响对象引用本身的地址。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>说说看你认为Java的优势是什么</title>
        <link>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E7%9C%8B%E4%BD%A0%E8%AE%A4%E4%B8%BAjava%E7%9A%84%E4%BC%98%E5%8A%BF%E6%98%AF%E4%BB%80%E4%B9%88/</link>
        <pubDate>Thu, 25 Dec 2025 13:12:33 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E8%AF%B4%E8%AF%B4%E7%9C%8B%E4%BD%A0%E8%AE%A4%E4%B8%BAjava%E7%9A%84%E4%BC%98%E5%8A%BF%E6%98%AF%E4%BB%80%E4%B9%88/</guid>
        <description>&lt;p&gt;我认为Java的优势主要有以下几点：&lt;/p&gt;
&lt;p&gt;1.因为有JVM这个充当翻译官的角色存在，程序员编写的代码编译成字节码后，不管是在Windows、Linux还是Mac系统上，JVM都可以把它翻译成机器可以读懂的语言，也就是可以实现&lt;strong&gt;跨平台&lt;/strong&gt;，在企业的开发中，便于部署。&lt;/p&gt;
&lt;p&gt;2.与C++需要手动的申请以及释放内存不同的是，Java自带了&lt;strong&gt;垃圾回收&lt;/strong&gt;机制（GC），可以实现自动在后台帮程序员打扫内存垃圾，这样的话程序员的精力可以更多的倾向于业务逻辑。&lt;/p&gt;
&lt;p&gt;3.Java的&lt;strong&gt;生态圈&lt;/strong&gt;十分的庞大，可以说不管你想做什么功能，基本上你都能找到像Spring全家桶那样现成的并且是成熟的开源库，这样你就不用重复的去造轮子。&lt;/p&gt;
&lt;p&gt;4.与面向过程的编程语言不同，Java是一种&lt;strong&gt;面向对象&lt;/strong&gt;的编程语言，具备封装、继承和多态这3个特性。这种面向对象的特性，使得企业在开发和维护那种比较大规模、并且复杂度较高的系统时会相对容易一些。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>什么是Java的多态特性</title>
        <link>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFjava%E7%9A%84%E5%A4%9A%E6%80%81%E7%89%B9%E6%80%A7/</link>
        <pubDate>Wed, 24 Dec 2025 18:25:20 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/%E4%BB%80%E4%B9%88%E6%98%AFjava%E7%9A%84%E5%A4%9A%E6%80%81%E7%89%B9%E6%80%A7/</guid>
        <description>&lt;p&gt;Java的多态指的是&lt;strong&gt;父类的引用指向子类的对象，使得在调用同一个方法时，会执行子类自己的实现&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;通常来说，多态有3个必要条件。&lt;/p&gt;
&lt;p&gt;1.要有继承关系，比如子类继承父类，或者实现关系，比如类实现接口；&lt;/p&gt;
&lt;p&gt;2.子类需要重写父类的方法；&lt;/p&gt;
&lt;p&gt;3.父类的引用指向子类的对象，换句话说就是把子类类型的对象赋值给父类类型的引用变量，也就是&lt;strong&gt;向上转型&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在面试的时候，有些面试官可能会问你&lt;strong&gt;多态的作用&lt;/strong&gt;，多态的作用就两个：&lt;/p&gt;
&lt;p&gt;一个是解耦，也就是调用方只依赖父类或者接口，并不会依赖它的具体实现。&lt;/p&gt;
&lt;p&gt;另一个是可拓展，就是说可以在完全不修改原有代码的情况下新增一个子类。&lt;/p&gt;
&lt;h2 id=&#34;多态性的实现方式&#34;&gt;多态性的实现方式
&lt;/h2&gt;&lt;p&gt;在面向对象的编程中，多态性有&lt;strong&gt;编译时多态&lt;/strong&gt;和&lt;strong&gt;运行时多态&lt;/strong&gt;两种实现方式。&lt;/p&gt;
&lt;p&gt;编译时多态也称作&lt;strong&gt;静态多态&lt;/strong&gt;，通过&lt;strong&gt;方法重载&lt;/strong&gt;来实现，Java编译器会在编译时根据方法调用时传入的参数类型和数量来决定具体调用哪一个重载方法。&lt;/p&gt;
&lt;p&gt;运行时多态也称作&lt;strong&gt;动态多态&lt;/strong&gt;，通过&lt;strong&gt;方法重写&lt;/strong&gt;来实现，当父类的引用调用方法时，在运行时会根据对象的实际类型来决定实际执行的子类重写之后的方法。&lt;/p&gt;
&lt;p&gt;其中，&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法重载&lt;/strong&gt;指的是&lt;strong&gt;同一个类&lt;/strong&gt;的多个方法，它们的名称相同但是参数列表不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方法重写&lt;/strong&gt;指的是&lt;strong&gt;子类&lt;/strong&gt;重写了&lt;strong&gt;父类&lt;/strong&gt;的一个或者多个方法。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java中的Exception和Error有什么区别</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84exception%E5%92%8Cerror%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</link>
        <pubDate>Wed, 24 Dec 2025 16:50:46 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84exception%E5%92%8Cerror%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/</guid>
        <description>&lt;p&gt;在Java中，Exception和Error两者都是&lt;strong&gt;Throwable&lt;/strong&gt;类的子类。&lt;/p&gt;
&lt;p&gt;总体上来说，&lt;strong&gt;Exception&lt;/strong&gt;是指可以被处理的程序异常，属于业务逻辑的范畴，常见的异常有NullPointerException和IOException等等；&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;Error&lt;/strong&gt;则指的是系统级的不可恢复的错误，错误出现以后一般就意味着进程需要终止或者重启，常见的错误有OutOfMemoryError、StackOverflowError等等。&lt;/p&gt;
&lt;p&gt;如果细分的话，Exception又可以分为Checked Exception和Unchecked Exception，也就是&lt;strong&gt;受检异常&lt;/strong&gt;和&lt;strong&gt;非受检异常&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;常见的&lt;strong&gt;受检异常&lt;/strong&gt;有IOException和SQLException，它们都是从Exception继承过来但是并不继承RuntimeException，在编译的时候必须使用try-catch块或者throws声明进行抛出这样的显式处理。&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;非受检异常&lt;/strong&gt;如NullPointerException和IndexOutOfBoundsException都是继承自RuntimeException，它们就不需要进行显式的处理，而是运行的时候才会抛出。&lt;/p&gt;
&lt;h3 id=&#34;异常处理时需要注意的点&#34;&gt;异常处理时需要注意的点：
&lt;/h3&gt;&lt;p&gt;1.如果什么异常都使用通用Exception，而不是特定的异常，那么其他同事在分析这段代码时就无法一眼看出这段代码&lt;strong&gt;实际想要捕获的异常&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;所以，尽量&lt;strong&gt;不要捕获类似于Exception这样的通用异常，而应该捕获特定的异常&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;2.如果我们在捕获异常之后不把异常进行抛出或者使用catch之后再用e.printStackTrace()输出一个标准错误流，这样操作的话也许这只是一个非常简单的bug，但是你仍然不知道是哪里出错了以及出错的原因。&lt;/p&gt;
&lt;p&gt;所以，我们可以按需自定义一定的格式，&lt;strong&gt;将详细的信息输入到日志系统中，这样的话便于后续进行错误的排查&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;3.想象这样一种场景，如果一个方法，这个方法内部调用了其他好几个方法，现在这个最开始的方法的某个参数传入了一个null值，但是，你并没有立刻处理这个情况，而是在这个最开始的方法调用了好几个方法之后才爆出这里其实最开始就有一个空指针。&lt;/p&gt;
&lt;p&gt;这样就会造成其实你明明只需要在最开始只抛出一点点信息就能够定位到这个错误所在的地方，但是当最开始的方法调用了其他好几个方法之后你再抛出的可能就是很多的堆栈信息。&lt;/p&gt;
&lt;p&gt;因此 ，最好&lt;strong&gt;不要延迟处理异常&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;4.try-catch中的代码会影响JVM对代码的优化，因此，使用try-catch块时，不推荐try住一大段代码，而是在必要的代码段使用try-catch，也就是说&lt;strong&gt;try-catch的范围应该尽量小&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;5.在控制程序的流程这一块，我们一般采取的原则是能用if/else语句来控制的话就不要用异常，直白点说就是&lt;strong&gt;不要通过异常来控制程序的流程&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;6.在finally中return或者处理返回值往往会发生覆盖try中的return的情况，因此，最好&lt;strong&gt;不要在finallly中处理返回值或者return&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Java中的序列化和反序列化是什么</title>
        <link>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%98%AF%E4%BB%80%E4%B9%88/</link>
        <pubDate>Wed, 24 Dec 2025 15:37:56 +0000</pubDate>
        
        <guid>https://iamxurulin.github.io/p/java%E4%B8%AD%E7%9A%84%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%98%AF%E4%BB%80%E4%B9%88/</guid>
        <description>&lt;p&gt;先来说说&lt;strong&gt;序列化&lt;/strong&gt;，序列化是把内存中的对象转变成二进制的数据，通过这种形式才能存储到硬盘或者发送给别人。&lt;/p&gt;
&lt;p&gt;再来说说&lt;strong&gt;反序列化&lt;/strong&gt;，由于硬盘和网络都只认识二进制的0和1，不认识Java对象，所以必须进行转换，把二进制的数据变回内存中的对象。&lt;/p&gt;
&lt;p&gt;在具体的实现细节上，有&lt;strong&gt;怎么做、怎么防、怎么稳&lt;/strong&gt;这3个问题面试官比较喜欢问。&lt;/p&gt;
&lt;p&gt;对于怎么做这个问题，一个类要想实现序列化，就必须实现Serializable接口，这个接口就相当于一个许可证。&lt;/p&gt;
&lt;p&gt;至于怎么防，如果有些像密码这种的字段，它不想被保存下来，我们可以通过加上一个transient关键字，这样的话，序列化的时候就会自动跳过这些字段。&lt;/p&gt;
&lt;p&gt;对于怎么稳，为了防止你修改了代码之后，在读取旧数据时发生报错，可以加上一个serialVersionUID版本号，这个版本号就相当于一个文件的身份证或者指纹。&lt;/p&gt;
&lt;p&gt;这个serialVersionUID可以用来验证序列化的对象和反序列化之后对应的对象的ID两者是否一致。&lt;/p&gt;
&lt;p&gt;举个例子说明一下：&lt;/p&gt;
&lt;p&gt;比如，如果在没有定义一个serialVersionUID的前提下，接着序列化一个对象，在反序列化之前，在对象中增加了一个成员变量使得对象的类结构发生了改变，这个时候进行反序列化就会失败。原因就是serial VersionUID已经不一致了。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
