实测应用层重试和 Mesh 重试叠加的后果:一跳上两层都重试 2 次,后端收到 9 倍的请求;链上三层应用都重试是 27 倍,应用与 Envoy 都重试是 243 倍;Envoy 默认 max_retries 3 会拦下一部分重试;retry_on 5xx 会让已写入的 POST 再写一次(200 次写入对 100 个请求);超时后放弃的请求后端仍在处理。
实测 Ingress 和 Gateway API(Traefik v3.7.15,同一个数据面):Ingress 只能按 host 和 path 选一个后端,同一路径写两条规则全部去了同一个后端;HTTPRoute 的权重(90/10 得到 450/50)和请求头匹配是规范字段;跨命名空间要过 allowedRoutes 和 ReferenceGrant;HTTPRoute 状态会说明错在哪,Ingress 不会;两种写法的生效都在 0.3 s 内。
实测 ApplicationContext.refresh() 的 12 个步骤:BeanDefinition 在 invokeBeanFactoryPostProcessors 里从 6 个涨到 15 个(最慢的一步,76456 µs);BeanFactoryPostProcessor、BeanPostProcessor 本身最先被创建;普通单例在 finishBeanFactoryInitialization 里创建,prototype 与 @Lazy 的 Bean 不创建;refresh 失败时已创建的单例会被销毁。
实测 BeanDefinition:手写、@Component 扫描、@Bean 方法三种来源的定义类型不同(@Bean 的定义类是 null、只有 factoryMethod);BeanFactoryPostProcessor 能在创建之前把 scope 改成 prototype;子定义会和 abstract 父定义合并成 RootBeanDefinition;同名定义在纯 Spring 里默认悄悄覆盖,SpringApplication 的上下文默认不允许。
实测 Bean 生命周期的 15 个创建回调的顺序:BPP.beforeInstantiation → 构造器 → populate → 各种 Aware → BPP.before → @PostConstruct → afterPropertiesSet → initMethod → BPP.after;销毁顺序 @PreDestroy → destroy() → destroyMethod;prototype 关闭容器时 0 个销毁回调;@Bean 默认推断 close();初始化失败的 Bean 不会调用 @PreDestroy。
实测容器的两个扩展点:BeanPostProcessor 能把 Bean 换成 JDK 代理(按具体类注入就失败);多个 BPP 按 PriorityOrdered、Ordered、无顺序三组执行;被提前创建的 Bean(Early 构造器依赖 Service)不会被后注册的 BPP 处理,Spring 会打 WARN;BFPP 的 @Bean 方法非 static 时,配置类里的 @Autowired 字段是 null。
实测依赖注入的写法与候选解析:构造器、setter、字段三种方式 new 出来的差别;两个候选时 NoUniqueBeanDefinitionException,参数名、@Qualifier、@Primary、@Priority 各自能解决;两个 @Primary 仍冲突;@Order 只影响 List 的顺序;没有候选时单个依赖失败、List 是空的;泛型参数参与匹配。
实测循环依赖:setter 循环能解开但 B 初始化时看到的 A 是半成品;构造器循环抛 BeanCurrentlyInCreationException;SpringApplication 默认禁止循环引用;自定义 BPP 包装让 SA、SB 各被构造了 2 次;Spring AOP 的循环会提前暴露代理;@Lazy 可以打破构造器循环。
实测作用域:单例里直接注入 prototype 只在创建时注入一次(id 恒为 1);ObjectProvider、@Lookup 每次拿新对象;prototype 加 scoped proxy 是每次方法调用换一个对象;thread 作用域加 scoped proxy 才是按线程稳定;单例里的 int 字段 8 线程各 200000 次 n++ 丢了 100 万次以上。
实测 @Configuration 的 full 模式与 lite 模式:@Bean 方法互相调用时 full 模式只构造 1 次 A、lite 模式构造 3 次;static @Bean 不被拦截;改用方法参数注入最安全;300 个配置类把 proxyBeanMethods 设成 false,refresh 从 847 ms 降到 475 ms、少加载 1174 个类。
实测组件扫描:3000 个 @Component 的 scan() 是 220 ms,但扫描时新加载的类始终是 205 个(类到实例化时才加载);包里每多一个没有注解的普通类也要被读(8000 个比 0 个多花约 357 ms,401 减 44 算出的);主类放在默认包会扫描整个 classpath 导致启动失败;同名 @Component 抛 ConflictingBeanDefinitionException。
实测 @Profile 与 @Conditional:没有激活 profile 时默认是 default,一旦激活别的 default 就失效;"dev & !cloud" 这样的表达式可用;“registry 里没有才注册”的条件依赖配置类顺序(User 先 [dataSource],Fallback 先 [dataSource, fallbackDs]);类级别条件在 PARSE_CONFIGURATION 阶段求值,看不到 @Bean 的定义;类上的条件会一次跳过 @Import 和嵌套配置类。