<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spring on Coder_Studio</title>
        <link>https://iamxurulin.github.io/tags/spring/</link>
        <description>Recent content in Spring 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/tags/spring/index.xml" rel="self" type="application/rss+xml" /><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>【面试真题拆解】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>【面试真题拆解】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>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>
        
    </channel>
</rss>
