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

如何使用Autofac实现责任链设计模式?含依赖注入配置问题

Autofac实现责任链模式的配置方案

问题背景

尝试用Autofac实现责任链设计模式,运行时收到Autofac组件未注册的提示。已知可以用RegisterDecorator配置,但该方法不适用于所有责任链场景,想了解如果使用SetNextHandler方法时该如何配置,以及是否有更合适的替代方案。

先修正代码中的错误

你的ConcreteHandlerB构造函数存在笔误,会导致编译失败,需修正为:

namespace ChainOfResponsibilitySample;

internal class ConcreteHandlerB : IHandler {
    private readonly IHandler _next;

    public ConcreteHandlerB(IHandler next) { // 原代码误写为ConcreteHandlerA
        _next = next;
    }

    public void Handle() {
        // do something
        _next.Handle();
    }
}

当前注册方式的问题

你使用的WithParameter直接解析IHandler会触发循环依赖:当Autofac解析ConcreteHandlerA时,需要注入IHandler,而最后注册的ConcreteHandlerA是IHandler的默认实现,会引发递归解析,最终抛出组件未注册或循环依赖异常。

解决方案

方案1:命名服务手动组装责任链(适合灵活控制顺序/使用SetNextHandler的场景)

这种方式完全手动控制链的组装逻辑,适合需要动态调整顺序、或者处理器通过SetNextHandler方法设置下一个节点的场景:

修改AutofacModule注册逻辑

using Autofac;

namespace ChainOfResponsibilitySample;

internal class AutofacModule : Module {
    protected override void Load(ContainerBuilder builder) {
        // 1. 将每个处理器注册为命名服务,避免覆盖默认IHandler
        builder.RegisterType<ConcreteHandlerC>().Named<IHandler>("HandlerC");
        builder.RegisterType<ConcreteHandlerB>().Named<IHandler>("HandlerB");
        builder.RegisterType<ConcreteHandlerA>().Named<IHandler>("HandlerA");

        // 2. 手动组装责任链:A → B → C
        builder.Register(ctx => {
            var handlerC = ctx.ResolveNamed<IHandler>("HandlerC");
            var handlerB = ctx.ResolveNamed<IHandler>("HandlerB", new TypedParameter(typeof(IHandler), handlerC));
            var handlerA = ctx.ResolveNamed<IHandler>("HandlerA", new TypedParameter(typeof(IHandler), handlerB));
            return handlerA;
        }).As<IHandler>(); // 将组装好的根处理器注册为IHandler默认实现

        base.Load(builder);
    }
}

适配SetNextHandler的场景

如果处理器通过SetNextHandler方法而非构造函数注入下一个节点,修改组装逻辑即可:

// 假设处理器接口新增SetNextHandler方法
public interface IHandler {
    void Handle();
    void SetNextHandler(IHandler next);
}

// 组装时调用方法设置下一个节点
builder.Register(ctx => {
    var handlerC = ctx.ResolveNamed<IHandler>("HandlerC");
    var handlerB = ctx.ResolveNamed<IHandler>("HandlerB");
    handlerB.SetNextHandler(handlerC);
    
    var handlerA = ctx.ResolveNamed<IHandler>("HandlerA");
    handlerA.SetNextHandler(handlerB);
    
    return handlerA;
}).As<IHandler>();

方案2:使用RegisterDecorator(适合固定顺序的装饰器式责任链)

RegisterDecorator适合无状态、顺序固定的责任链场景,本质是装饰器模式的实现,代码更简洁:

protected override void Load(ContainerBuilder builder) {
    // 注册最终节点
    builder.RegisterType<ConcreteHandlerC>().As<IHandler>();
    // 用B装饰现有IHandler(即C)
    builder.RegisterDecorator<IHandler>((ctx, inner) => new ConcreteHandlerB(inner), fromKey: null);
    // 用A装饰现有IHandler(即B→C)
    builder.RegisterDecorator<IHandler>((ctx, inner) => new ConcreteHandlerA(inner), fromKey: null);
}

注意:这种方式要求责任链是严格的装饰器结构(每个处理器仅依赖下一个节点),如果需要动态调整顺序、或者处理器有状态需要单独管理,方案1更合适。

方案3:注册顺序+InstancePerDependency(不推荐)

如果坚持用构造函数注入且不使用命名服务,可以调整注册顺序并指定生命周期,但这种方式依赖注册顺序,后续维护容易出错:

protected override void Load(ContainerBuilder builder) {
    // 先注册最终节点,InstancePerDependency确保每次解析都是新实例
    builder.RegisterType<ConcreteHandlerC>()
           .As<IHandler>()
           .InstancePerDependency();
    
    // 注册B,解析IHandler时会拿到C
    builder.RegisterType<ConcreteHandlerB>()
           .As<IHandler>()
           .InstancePerDependency()
           .WithParameter((pi, ctx) => pi.ParameterType == typeof(IHandler), 
                          (pi, ctx) => ctx.Resolve<IHandler>());
    
    // 注册A,解析IHandler时会拿到B
    builder.RegisterType<ConcreteHandlerA>()
           .As<IHandler>()
           .InstancePerDependency();
}

内容的提问来源于stack exchange,提问作者fizch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:12:25