Spring Security中Deferring/Deferred模式的原理与优势探究
近期研究Spring Security框架时,发现多个类和方法命名包含Deferring-或Deferred-前缀,例如DeferredSecurityContext、SupplierDeferredSecurityContext、DeferringObservationReactiveAuthorizationManager等。这类模式普遍使用Supplier函数式接口,通过供应商函数延迟获取对象,相关代码示例如下:
final class DeferringObservationAuthorizationManager<T> implements AuthorizationManager<T> { private final Supplier<AuthorizationManager<T>> delegate; DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider, AuthorizationManager<T> delegate) { this.delegate = SingletonSupplier.of(() -> { ObservationRegistry registry = (ObservationRegistry)provider.getIfAvailable(() -> { return ObservationRegistry.NOOP; }); return (AuthorizationManager)(registry.isNoop() ? delegate : new ObservationAuthorizationManager(registry, delegate)); }); } public AuthorizationDecision check(Supplier<Authentication> authentication, T object) { return ((AuthorizationManager)this.delegate.get()).check(authentication, object); } }
public class HttpSessionSecurityContextRepository implements SecurityContextRepository { // ... public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) { Supplier<SecurityContext> supplier = () -> { return this.readSecurityContextFromSession(request.getSession(false)); }; return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy); } } final class SupplierDeferredSecurityContext implements DeferredSecurityContext { private final Supplier<SecurityContext> supplier; // ... public SecurityContext get() { this.init(); return this.securityContext; } public boolean isGenerated() { this.init(); return this.missingContext; } private void init() { if (this.securityContext == null) { this.securityContext = (SecurityContext)this.supplier.get(); this.missingContext = this.securityContext == null; if (this.missingContext) { this.securityContext = this.strategy.createEmptyContext(); if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Created %s", this.securityContext)); } } } } }
针对这类模式,以下是具体问题的解答:
1. 该模式的设计目的是什么?
核心是延迟对象的初始化或获取时机,直到真正需要使用该对象的时候才执行创建/获取逻辑,而非在类实例化阶段就完成。这种设计主要是为了适配Spring生态中的特定场景需求,比如依赖的懒加载、运行时上下文的动态变化、资源的按需分配等。
2. 使用该模式能获得哪些优势?
- 避免不必要的资源消耗:如果某个对象在整个生命周期内可能不会被用到,延迟初始化可以省去创建它的开销,比如当
ObservationRegistry是NOOP实现时,就不需要额外包装成ObservationAuthorizationManager。 - 适配动态依赖场景:有些依赖对象可能在类初始化时还未完全就绪(比如Spring容器中的懒加载Bean),或者需要根据运行时上下文动态决定具体实现,
Supplier可以在调用get()时才去获取最新的依赖状态。 - 提升启动性能:减少应用启动阶段的初始化工作,把部分对象的创建延迟到第一次使用时,缩短启动时间,这对大型Spring应用尤为重要。
- 简化条件逻辑:可以把复杂的条件判断(比如是否需要包装代理类)封装在
Supplier的Lambda中,避免在类构造阶段就处理复杂分支,让代码结构更清晰。
3. 解决实际问题的示例
示例1:Observation授权管理器的动态创建
在DeferringObservationAuthorizationManager中,构造函数并未直接创建ObservationAuthorizationManager,而是把创建逻辑封装在SingletonSupplier中。只有当第一次调用check()方法时,才会执行delegate.get(),此时才会判断ObservationRegistry是否为NOOP:
- 如果是NOOP,直接返回原始的
AuthorizationManager,避免了不必要的代理包装; - 如果不是NOOP,才创建
ObservationAuthorizationManager来实现监控功能。
这种方式避免了在启动阶段就创建可能用不上的代理对象,节省了初始化资源。
示例2:SecurityContext的按需加载
在HttpSessionSecurityContextRepository的loadDeferredContext方法中,返回的SupplierDeferredSecurityContext并没有立刻从Session中读取SecurityContext,而是把读取逻辑放在Supplier里。只有当调用get()或isGenerated()方法时,才会执行init()去读取Session中的上下文:
- 如果后续逻辑不需要用到
SecurityContext,就不会触发Session读取操作,减少了不必要的IO和Session交互; - 即使需要读取,也能保证是在当前请求的上下文下去获取最新的Session状态,避免了提前读取导致的上下文不一致问题。
内容的提问来源于stack exchange,提问作者Junhyunny

