26、Using Alternative DI Containers 替换DI容器
EasyNetQ是由独立组件的集合组成的。在内部,它使用了一个称为DefaultServiceProvider的微型内部DI(IoC)容器(EasyNetQ作者自己写的一个)。如果你看一下静态RabbitHutch类的代码(你用来创建IBus实例的那个),您将看到它只是创建了一个新的DefaultServiceProvider实例,注册所有EasyNetQ的组件,然后调用容器的Resolve() 方法,使用容器提供的依赖关系树创建一个IBus的新实例:
public static IBus CreateBus(IConnectionConfiguration connectionConfiguration, Action<IServiceRegister> registerServices) { Preconditions.CheckNotNull(connectionConfiguration, "connectionConfiguration"); Preconditions.CheckNotNull(registerServices, "registerServices"); var container = createContainerInternal(); if (container == null) { throw new EasyNetQException("Could not create container. " + "Have you called SetContainerFactory(...) with a function that returns null?"); } registerServices(container); container.Register(_ => connectionConfiguration); ComponentRegistration.RegisterServices(container); return container.Resolve<IBus>(); //从容器中拿出IBus的实例 }
但是如果您希望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();
完。