You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security中Deferring/Deferred模式的原理与优势探究

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:26:09