3

IoC 容器的一个优点是您可以在对象图的底部交换模拟服务。然而,这似乎在 Spring.Net 中比在其他 IoC 容器中更难做到。这是一些在 Unity 中执行并具有 Spring.Net 代码的代码;

namespace IocSpringDemo
{
    using Microsoft.Practices.Unity;
    using NUnit.Framework;

    using Spring.Context;
    using Spring.Context.Support;

    public interface ISomeService
    {
        string DoSomething();
    }

    public class ServiceImplementationA : ISomeService
    {
        public string DoSomething()
        {
            return "Hello A";
        }
    }

    public class ServiceImplementationB : ISomeService
    {
        public string DoSomething()
        {
            return "Hello B";
        }
    }

    public class RootObject
    {
        public ISomeService SomeService { get; private set; }

        public RootObject(ISomeService service)
        {
            SomeService = service;
        }
    }

    [TestFixture]
    public class UnityAndSpringDemo
    {
        [Test]
        public void UnityResolveA()
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<ISomeService, ServiceImplementationA>();
            RootObject rootObject = container.Resolve<RootObject>();
            Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void UnityResolveB()
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<ISomeService, ServiceImplementationB>();
            RootObject rootObject = container.Resolve<RootObject>();
            Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void SpringResolveA()
        {
            IApplicationContext container = ContextRegistry.GetContext();
            RootObject rootObject = (RootObject)container.GetObject("RootObject");
            Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
        }

        [Test]
        public void SpringResolveB()
        {
            // does not work - what to do to make this pass?
            IApplicationContext container = ContextRegistry.GetContext();
            RootObject rootObject = (RootObject)container.GetObject("RootObject");
            Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
        }
    }
}

为了使用 Spring,App.config 文件中需要包含以下内容。显然,这只适用于第一次春季测试,而不是第二次。你可以在配置文件中放置多个弹簧配置吗?如果是这样,语法是什么以及如何访问它们?还是有其他方法可以做到这一点?

  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>
    <objects xmlns="http://www.springframework.net">
      <object name="RootObject" type="IocSpringDemo.RootObject, IocDemo" autowire="constructor" />
      <object name="service" type="IocSpringDemo.ServiceImplementationA, IocDemo" autowire="constructor" />
    </objects>
  </spring>

更新

这是基于Marko Lahma 提供给 Mark Pollack 博客的链接中的代码的部分答案。我通过了上述测试,代码如下:

public static class SpringHelper
{
    public static T Resolve<T>(this IApplicationContext context, string name)
    {
        return (T)context.GetObject(name);
    }

    public static void RegisterType<T>(this GenericApplicationContext context, string name)
    {
        context.RegisterType(name, typeof(T));
    }

    public static void RegisterType(this GenericApplicationContext context, string name, Type type)
    {
        IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory();
        ObjectDefinitionBuilder builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, type);
        builder.SetAutowireMode(AutoWiringMode.AutoDetect);

        context.RegisterObjectDefinition(name, builder.ObjectDefinition);
    }
}

...

    [Test]
    public void SpringResolveA()
    {
        GenericApplicationContext container = new GenericApplicationContext();
        container.RegisterType<RootObject>("RootObject");
        container.RegisterType<ServiceImplementationA>("service");

        RootObject rootObject = container.Resolve<RootObject>("RootObject");
        Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
    }

    [Test]
    public void SpringResolveB()
    {
        GenericApplicationContext container = new GenericApplicationContext();
        container.RegisterType<RootObject>("RootObject");
        container.RegisterType<ServiceImplementationB>("service");

        RootObject rootObject = container.Resolve<RootObject>("RootObject");
        Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
    }

这向我提出了几个问题:

  • 我想将此技术集成到使用常用容器的现有代码中。在这种情况下,为什么我必须使用不同的容器类型GenericApplicationContext?如果我想从 app.config 或 web.config 中现有的 spring 配置中读取数据到这个对象中怎么办?它会像通常的上下文一样工作吗?然后我可以用代码在这些注册上写数据吗?

  • 如何指定ISomeService要创建为单例?我的意思不是向容器提供单例实例,而是容器用于创建实例、解析其构造函数并在需要该类型时使用它。

  • 我该怎么做container.RegisterType<ISomeService, ServiceImplementationA>();?我想注册类型映射以在构造函数需要该类型的所有情况下使用。

  • 具体是container.RegisterType<ServiceImplementationA>("service");做什么的?它似乎注册ServiceImplementationA为实现ISomeService但从ISomeService未被提及,因此可能存在歧义。例如,如果ServiceImplementationA实现了多个接口会怎样。

  • 给注册的字符串名称是什么?它不适用于 en 空字符串,但它似乎并不重要。

我是否试图以一种不起作用的方式使用弹簧?我正在尝试像使用其他 IoC 容器一样使用它,但它并不完全有效。

4

2 回答 2

3

这有点像苹果和橘子的比较,因为单元测试使用 Unity 的代码配置和 Spring.NET 的 XML (app.config) 配置。

如果您走 XML 路线,那么您可以注释掉旧的实现 A 并将 B 实现定义为要使用的实现 - 什么是正确的配置?其他选项是为每个场景(配置设置)提供专用的 XML 文件,并通过上下文的资源定义包含它们(您现在有内联资源)。其他选项包括文件系统和程序集,请参阅Spring.NET 手册中的 Web 配置部分以获取一个很好的示例。

如果您采用代码配置路线,我建议您检查Spring.NET Recoil和即将推出的 CodeConfig

于 2009-12-24T10:28:03.663 回答
3

添加作为新答案试图解决开放点......

我想将此技术集成到使用常用容器的现有代码中。为什么在这种情况下我必须使用不同的容器类型 GenericApplicationContext?如果我想从 app.config 或 web.config 中现有的 spring 配置中读取数据到这个对象中怎么办?它会像通常的上下文一样工作吗?然后我可以用代码在这些注册上写数据吗?

Spring 为不同类型的初始化策略提供了具体的应用程序上下文实现。最常用的是 GenericApplicationContext(手动)、XmlApplicationContext(XML 文件)和 WebApplicationContext(非常类似于 XmlApplicationContext,但为 Web 使用量身定制)。它们都实现了通用接口:IApplicationContext,这是访问这些容器的首选方式。

不幸的是,使用代码更改注册通常意味着您需要直接使用特定的子类。使用 GenericApplicationContext 和 StaticApplicationContext 这很自然,但 XmlApplicationContext 通常被认为是仅 XML,并且这种方式“固定”到 XML 定义。

如何指定将 ISomeService 创建为单例?我的意思不是向容器提供单例实例,而是容器创建实例、解析其构造函数并在需要该类型时使用它。

您的 SpringHelper 就是这样做的,默认情况下 Spring 中的所有对象都是单例的。您可以通过使用 false 调用 ObjectDefinitionBuilder 的 SetSingleton 方法来更改此行为。

我该如何做相当于 container.RegisterType(); ? 我想注册类型映射以在构造函数需要该类型的所有情况下使用。

Spring 使用对象名称(id)来区分不同的实现。因此,如果您想获得特定类型以服务特定实例,以防有很多替代方案,您应该按名称引用此特定实例。如果您正在使用自动装配并且您的对象依赖于接口 ISomeService 并且只有一个注册的对象实现了它,那么自动装配可以毫无歧义地设置它。

container.RegisterType("service"); 到底是什么?做?似乎将 ServiceImplementationA 注册为 ISomeService 的实现,但从未提及 ISomeService,因此可能存在歧义。例如,如果 ServiceImplementationA 实现了多个接口怎么办。

继续上一个答案,这将注册类型为 ServiceImplementationA 的单例,名称为“service”。这个对象带有所有它实现的接口(当然还有它的具体类型)的自动装配候选者。

给注册的字符串名称是什么?它不适用于 en 空字符串,但它似乎并不重要。

如前所述,这很重要。该名称是该上下文中的唯一 ID(父上下文可能具有相同名称的对象)并且可用于访问特定对象注册。简而言之,在其他框架可能将类型关联为对象注册的键时,Spring 使用名称。

于 2010-01-13T07:03:48.007 回答