实测 Environment 与 PropertySource:系统属性 > 环境变量 > @PropertySource 文件,addFirst 赢、addLast 同名键输;MY_NAME 环境变量可以用 my.name 取;没有 PropertySourcesPlaceholderConfigurer 时缺失的 @Value 会被原样注入字面量;yes/on/1 都被转成 true;@PropertySource 读中文 .properties 默认乱码。
实测 FactoryBean 与 ObjectProvider:FactoryBean 的产品在第一次 getBean 才创建,"&名字" 取到工厂本体;isSingleton 为 false 时每次 getBean 都新建;SmartFactoryBean 的产品容器不缓存(源码里有注释);getObjectType 返回 null 就按类型找不到;ObjectProvider 在没有、一个、两个候选时 getIfAvailable / getIfUnique 的结果不同,orderedStream 按 @Order 排序,它只推迟 prototype 的创建,不推迟 singleton。
实测 Spring 事件机制:默认同步,监听器在发布者线程里依次执行,一个失败后面的跳过且异常传回发布者;多播器配线程池后发布者 0 ms 返回、异常交给 ErrorHandler;泛型事件要实现 ResolvableTypeProvider 才按泛型匹配;在 @PostConstruct 里发布的事件会被 @EventListener 方法错过,ApplicationListener 的 Bean 却能收到。
实测 SpEL:@Value("#{...}") 的引用 Bean、安全导航、静态方法调用、集合投影与过滤;null 上取属性抛 EL1007E;StandardEvaluationContext 能调 Runtime 的静态方法,SimpleEvaluationContext 拒绝;每次重新解析表达式约 1300 ns,复用解析结果约 96 ns,编译模式约 5 到 7 ns。
实测 Spring 的类型转换:DefaultConversionService 能转基本类型、枚举(大小写敏感)和集合,不含 java.time 与 Duration;Converter 只管一个方向,Formatter 同时管 parse 与 print;DataBinder 把写错的字段变成 typeMismatch 错误并保持默认值;给绑定器注册的 PropertyEditor 优先于 ConversionService。
实测 Resource 抽象:没有前缀的路径按 classpath 找而不是文件系统;classpath: 只取第一个匹配,classpath*: 把 3 个 jar 里的 spring.factories 都找出来;jar 里的资源 getFile() 抛 FileNotFoundException、getInputStream() 正常;ClassPathResource 只读,InputStreamResource 只能读一次。
实测 SmartLifecycle:启动按 phase 从小到大、停止从大到小,所有 stop 完成之后才执行 @PreDestroy;refresh 只自动启动 isAutoStartup 为 true 的,context.start() 会启动所有 Lifecycle;同 phase 的异步停止并行(308 ms),不同 phase 串行(918 ms);不回 callback 的 Bean 会拖到 timeoutPerShutdownPhase(1000 ms)才继续。
实测 @Lazy:50 个构造要 20 ms 的单例,饿汉 refresh 1263 ms、懒加载 86 ms,但第一次取其中 5 个要 127 ms;配置错误在懒加载下推迟到第一次使用才抛出;注入点 @Lazy 只推迟解析,目标和注入点两处都 @Lazy 才推迟创建;带 @EventListener 的 Bean 不会被 @Lazy 推迟;SpringApplication.setLazyInitialization(true) 让单例从 22 个变成 20 个。
实测 Spring AOP 的代理选择:纯 Spring 里有接口的类默认用 JDK 代理(instanceof 实现类为 false、按具体类取 Bean 失败),没有接口用 CGLIB;proxyTargetClass = true 全部走 CGLIB;Spring Boot 不设任何属性也默认走 CGLIB;创建 2000 个代理 JDK 约 130 ms、CGLIB 约 70 ms,每次调用两者都约 40 ns。
实测切点表达式匹配:execution、within、@within、@annotation、target、args、bean 各自命中哪些方法;自调用的内部方法不命中;CGLIB 代理下包可见和 protected 方法也被拦截;带 args、@annotation、target 的切点每次调用比单纯 execution 贵约一倍;静态就不可能匹配的切点不创建代理。
实测通知执行顺序:切面按 @Order 从外到内包裹,同一个切面里是 @Around → @Before → 目标 → @AfterReturning(或 @AfterThrowing)→ @After;@Bean 定义顺序不影响有 @Order 的切面;同类型的两个 @Before 看起来按方法名排序;@Around 可以改参数或不调用 proceed();写在 @Bean 方法上的 @Order 对切面没有生效。
实测自调用失效:同一个类里 a() 调用 this.b(),切面对 b() 拦截 0 次(外部调用才是 1 次),因为 this 是目标对象不是代理;注入自己的代理、AopContext.currentProxy()(要开 exposeProxy)、拆成两个 Bean 都能把拦截次数变成 1;通过 CGLIB 代理读 public 字段得到 null。