1. 程式人生 > 實用技巧 >EasyNetQ筆記-替換IOC容器

EasyNetQ筆記-替換IOC容器

EasyNetQ是由獨立元件的集合組成的。在內部,它使用了一個稱為DefaultServiceProvider的微型內部DI(IoC)容器(EasyNetQ作者自己寫的一個)。如果你看一下靜態RabbitHutch類的程式碼(你用來建立IBus例項的那個),您將看到它只是建立了一個新的DefaultServiceProvider例項,註冊所有EasyNetQ的元件,然後呼叫容器的Resolve() 方法,使用容器提供的依賴關係樹建立一個IBus的新例項:

        //registerServices:外部通過此引數註冊服務
        public static IBus CreateBus(Func<IServiceResolver, ConnectionConfiguration> connectionConfigurationFactory, Action<IServiceRegister> registerServices)
        {
            var container = new DefaultServiceContainer();
            RegisterBus(container, connectionConfigurationFactory, registerServices);
            return container.Resolve<IBus>();
        }
            
        public static void RegisterBus(IServiceRegister serviceRegister,
                                       Func<IServiceResolver, ConnectionConfiguration> connectionConfigurationFactory,
                                       Action<IServiceRegister> registerServices)
        {
            serviceRegister.Register(c =>
            {
                var configuration = connectionConfigurationFactory(c);
                configuration.SetDefaultProperties();
                return configuration;
            });

            serviceRegister.RegisterDefaultServices();
            registerServices(serviceRegister);
        }
    public static class ServiceRegisterExtensions
    {
        /// <summary>
        ///     Registers the default EasyNetQ components
        /// </summary>
        /// <param name="serviceRegister">The register</param>
        public static void RegisterDefaultServices(this IServiceRegister serviceRegister)
        {
            Preconditions.CheckNotNull(serviceRegister, "container");

            // Note: IConnectionConfiguration gets registered when RabbitHutch.CreateBus(..) is run.
            // default service registration
            serviceRegister
                .Register<IConnectionStringParser, ConnectionStringParser>()
                .Register<ISerializer>(_ => new JsonSerializer())
                .Register<IConventions, Conventions>()
                .Register<IEventBus, EventBus>()
                .Register<ITypeNameSerializer, DefaultTypeNameSerializer>()
                .Register<ICorrelationIdGenerationStrategy, DefaultCorrelationIdGenerationStrategy>()
                .Register<IMessageSerializationStrategy, DefaultMessageSerializationStrategy>()
                .Register<IMessageDeliveryModeStrategy, MessageDeliveryModeStrategy>()
                .Register(new AdvancedBusEventHandlers())
                .Register<IProduceConsumeInterceptor, DefaultInterceptor>()
                .Register<IConsumerDispatcherFactory, ConsumerDispatcherFactory>()
                .Register<IExchangeDeclareStrategy, DefaultExchangeDeclareStrategy>()
                .Register<IConsumerErrorStrategy, DefaultConsumerErrorStrategy>()
                .Register<IErrorMessageSerializer, DefaultErrorMessageSerializer>()
                .Register<IHandlerRunner, HandlerRunner>()
                .Register<IInternalConsumerFactory, InternalConsumerFactory>()
                .Register<IConsumerFactory, ConsumerFactory>()
                .Register(c =>
                {
                    var connectionConfiguration = c.Resolve<ConnectionConfiguration>();
                    return ConnectionFactoryFactory.CreateConnectionFactory(connectionConfiguration);
                })
                .Register<IClientCommandDispatcher, SingleChannelClientCommandDispatcher>()
                .Register<IPersistentConnection, PersistentConnection>()
                .Register<IPersistentChannelFactory, PersistentChannelFactory>()
                .Register<IPublishConfirmationListener, PublishConfirmationListener>()
                .Register<IHandlerCollectionFactory, HandlerCollectionFactory>()
                .Register<IPullingConsumerFactory, PullingConsumerFactory>()
                .Register<IAdvancedBus, RabbitAdvancedBus>()
                .Register<IPubSub, DefaultPubSub>()
                .Register<IRpc, DefaultRpc>()
                .Register<ISendReceive, DefaultSendReceive>()
                .Register<IScheduler, ExternalScheduler>()
                .Register<IBus, RabbitBus>();
        }
}

但是如果您希望EasyNetQ使用您的選擇容器呢?從版本0.25,RabbitHutch類提供了一個靜態方法SetContainerFactory,它允許您註冊一個可選的容器工廠方法,你想怎麼實現EasyNetQ.IContainer介面都可以,只要通過該工廠方法提供給EasyNetQ即可。

Jeff Doolittle已經為Windsor and Structure Map創造了IoC容器封裝:

https://github.com/jeffdoolittle/EasyNetQ.DI

在這個例子中,我們使用了Castle Windsor IoC容器:

// 註冊我們替換的容器工廠
RabbitHutch.SetContainerFactory(() =>
    {
        // 建立一個Windsor IoC容器例項
        var windsorContainer = new WindsorContainer();

        // 把Windsor封裝到我們寫的實現了EasyNetQ.IContainer介面的封裝類物件中
        return new WindsorContainerWrapper(windsorContainer);
    });

// 現在我們建立一個IBus介面的例項,但這次它是從windsor IoC容器中解析出來的,
// 而不是像以前例子那樣,從EasyNetQ預設的IoC容器了。
var bus = RabbitHutch.CreateBus("host=localhost");

下面是WindsorContainerWrapper類,我用來封裝Windsor IoC容器的例項的,目的是實現EasyNetQ.IContainer介面,因為EasyNetQ裡訪問容器都是用的這個介面型別的例項(當然你也可以自己去寫一個封裝,愛封裝啥IoC就去封裝啥):

public class WindsorContainerWrapper : IContainer, IDisposable
{
    private readonly IWindsorContainer windsorContainer;

    public WindsorContainerWrapper(IWindsorContainer windsorContainer)
    {
        this.windsorContainer = windsorContainer;
    }

    public TService Resolve<TService>() where TService : class
    {//解析元件
        return windsorContainer.Resolve<TService>();
    }

    public IServiceRegister Register<TService>(System.Func<IServiceProvider, TService> serviceCreator) 
        where TService : class
    {//註冊元件
        windsorContainer.Register(
            Component.For<TService>().UsingFactoryMethod(() => serviceCreator(this)).LifeStyle.Singleton
            );
        return this;
    }

    public IServiceRegister Register<TService, TImplementation>() 
        where TService : class 
        where TImplementation : class, TService
    {
        windsorContainer.Register(
            Component.For<TService>().ImplementedBy<TImplementation>().LifeStyle.Singleton
            );
        return this;
    }

    public void Dispose()
    {
        windsorContainer.Dispose();
    }
}

注意在我的封裝中,所有EasyNetQ服務都註冊為singletons單例了。

恰當地清理Windsor很重要。EasyNetQ沒有在IContainer上定義Dispose 方法,但是您可以通過Advanced Bus訪問容器,這樣清理Windsor:

((WindsorContainerWrapper)bus.Advanced.Container).Dispose();
bus.Dispose();