单例里注入 prototype 为什么拿到的总是同一个?ObjectProvider、@Lookup、scoped proxy 各自怎么解决?scoped proxy 的代理是什么样?单例里放可变状态到底会怎样?这篇在 Spring Framework 7.0.9 上逐项量出来。
(a) 单例 Naive 里 @Autowired 了 prototype 的 Proto:连续 3 次 use() 得到的 id → 1,1,1(同一个对象:创建 Naive 时注入的那一个)
(b) ObjectProvider<Proto>.getObject():连续 3 次得到的 id → 2,3,4
直接 getBean(Proto) 两次的 id → 5,6单例只在创建时注入一次,之后一直是同一个对象。
(c) @Lookup:连续 3 次 use() 的 id → 1,2,3;LookupBean 的类 Main$LookupBean$$…(Spring 生成的 CGLIB 子类) (d) prototype + scoped proxy(TARGET_CLASS):注入的是代理?true;连续 3 次 p.id() → 1,2,3(每次方法调用都创建了新的目标对象);made=3
ObjectProvider 和 @Lookup 每次新对象;prototype 加 scoped proxy 是每次方法调用换对象。
单例 Holder 里的 ctx 是代理?true,实现了 ScopedObject?true 主线程连续 3 次 id → 1,1,1 另外 3 个线程各调用 2 次,拿到的 id(排序后)→ [2, 2, 3, 3, 4, 4];目标对象一共创建了 4 个(主线程 1 个 + 3 个线程各 1 个)
scoped proxy 把按线程变化的依赖变成稳定的引用:同线程同目标,不同线程不同目标。
proxyMode=INTERFACES,却按具体类 ThreadCtx 注入 → NoSuchBeanDefinitionException: No qualifying bean of type 'ThreadCtx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}INTERFACES 的代理不能按具体类注入。
单例里的 int 字段 n++:期望 1600000,实际 407345,丢了 1192655 次;同一个单例里的 AtomicInteger:1600000 单例里的 int 字段 n++:期望 1600000,实际 504079,丢了 1095921 次;同一个单例里的 AtomicInteger:1600000 单例里的 int 字段 n++:期望 1600000,实际 581252,丢了 1018748 次;同一个单例里的 AtomicInteger:1600000
单例被所有线程共享,可变字段会丢更新。