作者:苏渡苇
项目地址 :github.com/iweidujiang...(感谢 Star !)
一、加完依赖,谁帮你 new 的那些 Bean?
写 Spring Boot 的人都干过这种事:
xml
<dependency>
<groupId>io.github.iweidujiang</groupId>
<artifactId>spring-insight-agent-starter</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
然后 application.yml 里丢两行:
yaml
spring:
application:
name: my-service
insight:
server-url: http://localhost:9966
启动应用,日志里冒出一句:
text
[AgentStarter] Spring Insight Agent 已启用......
你自己的启动类上没写 @EnableSpringInsight,也没扫 io.github.iweidujiang 这个包。那些拦截器、上报器、配置类是怎么进容器的?
很多人会下意识猜:
大概 Spring 把第三方 jar 里的类全扫了一遍吧?
不是。要真这么干,classpath 上每个库的 @Configuration 都会往你应用里塞 Bean,那才叫灾难。
真正的机关,就藏在 Starter 里一个不怎么起眼的文本文件。
二、并非靠"组件扫描",而是"自动装配"
Spring 默认只扫你启动类所在包及其子包。
你的服务是 com.example.order.OrderApplication,Insight 的类在 io.github.iweidujiang.springinsight......。两边包名八竿子打不着,@SpringBootApplication 扫不到它们,这是故意的。
所以 加个 Starter 就能用,靠的不是扫描,靠的是 自动装配清单:Starter 自己报名------「启动的时候请把我这个配置类加载进去」。
Spring Boot 3 认的清单文件名有点长,但路径是固定的:
text
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Spring Insight 的业务侧 Starter 里,这个文件现在就两行:
text
io.github.iweidujiang.springinsight.agent.starter.InsightAgentAutoConfiguration
io.github.iweidujiang.springinsight.feign.InsightFeignTracingAutoConfiguration
没有 XML,没有魔法注解扫描全世界。就是全限定类名,一行一个。
你可以把它想成饭店后厨的「今日出餐表」:不是把仓库里所有菜都端上桌,只做表上写了的那几道。
三、Boot 启动时到底读了什么
流程其实很土,土到你用 jar tf 就能验证:
text
你的应用启动
→ Spring Boot 把依赖 jar 翻一遍
→ 找到所有 AutoConfiguration.imports
→ 按里面的类名把配置类注册进容器
→ 每个配置类上的 @ConditionalXxx 再决定:这次要不要真的生效
几点容易混:
1. 这不是老的 spring.factories
Boot 2.7 之前,自动配置写在:
text
META-INF/spring.factories
键是 org.springframework.boot.autoconfigure.EnableAutoConfiguration。
Boot 3 把自动配置清单拆成了上面那个 .imports 文件。
Insight 的 insight-agent 里还有 一个 spring.factories,但那是给 EnvironmentPostProcessor 用的,跟「报名 AutoConfiguration」不是一回事。
2. 清单要放在 Starter 里,别放在纯库里
insight-agent 是采集核心,谁依赖它谁都能用它的类;但报名自动装配的是 spring-insight-agent-starter。
这是 Starter 的老规矩:库本身尽量「老实」,不要因为被人间接依赖就自己往容器里塞东西。业务方显式加 Starter,才表示「我同意你自动起来」。
3. 报了名,不等于一定干活
清单只解决「配置类进不进容器」。进了之后,还要过 @ConditionalOnProperty、@ConditionalOnClass 这些关。关过不了,配置类就躺着,不捣乱。
四、对照 Spring Insight 的入口类
第一行对应的类长这样:
java
@AutoConfiguration
@EnableConfigurationProperties(InsightProperties.class)
@ConditionalOnProperty(
prefix = "spring.insight",
name = "enabled",
havingValue = "true",
matchIfMissing = true)
@Import(InsightAgentAutoConfigurationImportSelector.class)
public class InsightAgentAutoConfiguration {
public InsightAgentAutoConfiguration() {
log.info("[AgentStarter] Spring Insight Agent 已启用......");
}
}
四个注解各管一件事:
| 注解 | 人话 |
|---|---|
@AutoConfiguration |
告诉 Boot:我是自动配置,按自动配置的规则排序、加载 |
@EnableConfigurationProperties |
把 spring.insight.* 绑到 InsightProperties |
@ConditionalOnProperty |
开关。enabled=true 才干;没配也当 true (matchIfMissing = true) |
@Import(...) |
真正的拦截器、上报器不堆在这一个类里,交给选择器按 Web 栈再导入 |
最后那个 @Import 是下一篇的主角。本篇内容主要是自动装配:自动装配的门,是 .imports 文件推开的;门后面装什么,是这个配置类接着干的。
构造方法里打那行 info 日志,也是有用的土办法------Starter 这种「看不见的依赖」,最怕静悄悄失败。启动后搜 [AgentStarter],比猜「到底装上没有」省事。
五、为什么还要第二行:Feign 那个配置
.imports 里第二行是 InsightFeignTracingAutoConfiguration。它上面挂了:
java
@AutoConfiguration
@ConditionalOnWebApplication(type = SERVLET)
@ConditionalOnClass(Client.class) // feign.Client
public class InsightFeignTracingAutoConfiguration { ... }
翻译成人话:
- 你这个服务没有 OpenFeign →
feign.Client不在 classpath → 这个配置直接跳过,零影响 - 你是普通 MVC 服务,有 Feign → 才去给 Feign Client 套一层,方便后面画拓扑
Starter 的 pom 里,feign-core 是 optional。自动装配清单里把 Feign 单独挂一行,就是为了:有就用,没有就当这段代码不存在。
别把所有能力塞进一个巨型 @Configuration。条件越细,别人项目越不容易被你拖进坑里。
六、怎么确认它真的生效了(以及怎么关掉)
看日志。 启动后搜:
text
[AgentStarter] Spring Insight Agent 已启用
没有这行,先查三件事:依赖是不是 agent-starter 、是不是 Boot 3(.imports 这套)、jar 里到底有没有那个 META-INF/spring/...imports 文件。
看开关。 不想要了,不必删依赖,配一行就行:
yaml
spring:
insight:
enabled: false
matchIfMissing = true 的意思是:你懒得配,默认开;你明确说 false,就关。这比再搞一个「必须写 @EnableXxx 才启动」对使用者更友好。
看它不会替你做的事。 自动装配只保证「Bean 进容器」。数据往哪报、服务叫什么,还是配置说了算:
- 没配
spring.insight.server-url,HTTP 上报 Sink 根本不会建出来 - 没起
insight-server,报了也没人收 - 拓扑边还依赖 Feign 那层包装(有 Feign 才会走第二行配置)
「加依赖就生效」≠「加依赖就万事大吉」。生效的是装配,不是整个监测系统自己长出来。
七、自己做 Starter
如果哪天你也有需要做个 Starter,无需一上来就写 ImportSelector、别一上来就研究 AutoConfiguration 排序。做好如下三步即可:
- 单独一个
xxx-spring-boot-starter模块,不要让业务直接依赖你的核心 jar 然后指望它自动起来 - 在 Starter 里放
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,一行一个配置类 - 配置类上用
@ConditionalOnProperty/@ConditionalOnClass把「没有的能力」挡在门外,再留一个默认开、能关掉的开关
Insight 现在就是这个形状。
八、小结
| 你以为的 | 实际是 |
|---|---|
| 扫描了第三方包 | 只扫你自己的启动类包 |
| 某个注解偷偷全局生效 | .imports 文件点名加载配置类 |
| 加了 Starter 就等于功能全开 | 还要过 @Conditional*,还要配 server-url |
必须写 @EnableXxx |
有 Starter 时,注解是可选项 |
Spring Boot 的「约定大于配置」,落到 Starter 上就一句大白话:你把全限定类名写进那个文件,Boot 启动时会来读。
下一篇会接着讲门 后面的 @Import:为什么 Gateway 上不能把 MVC 的 HandlerInterceptor 硬塞进去,以及 DeferredImportSelector 在 Insight 里具体干了啥。
🌟 最后:欢迎围观 Spring Insight
如果你对「轻量监测 / Spring Boot Starter / 链路埋点」感兴趣,欢迎看看这个还在打磨的小项目:
🔗 GitHub :github.com/iweidujiang...
当前形态很朴素:业务服务加 spring-insight-agent-starter,旁边单独跑 insight-server 看拓扑和链路。能力有限,代码也还糙,适合当练手和对照。
你的 Star 是对我最大的鼓励。