如何更改 Spring.Net 中的配置

发布于 2024-08-16 03:49:34 字数 6100 浏览 7 评论 0原文

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 文件中需要包含以下内容。显然,这仅适用于第一次春季测试,而不适用于第二次。您可以在配置文件中放置多个 spring 配置吗?如果是这样,语法是什么以及如何访问它们?或者还有其他方法可以做到这一点吗?

  <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(); 等效的操作?我想注册类型映射,以便在构造函数需要该类型的所有情况下使用。

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

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

我是否尝试以一种不起作用的方式使用 spring?我尝试像其他 IoC 容器一样使用它,但它不太有效。

An advantage of an IoC container is that you can swap in a mock service at the bottom of your object graph. However this seems much harder to do in Spring.Net than in other IoC Containers. Here's some code that does it in Unity and has Spring.Net code;

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());
        }
    }
}

For the benefit of Spring, the following needed to be in the App.config file. Clearly this only serves the first spring test, and not the second. Can you put multiple spring configurations in the config file? If so, what is the syntax and how do you access them? Or is there another way to do this?

  <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>

Update

Here is a partial answer based at code at the links that Marko Lahma gave to Mark Pollack's blog. I have the above tests passing, with the following code:

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());
    }

This raises a few questions to me:

  • I want to integrate this technique into existing code that uses the usual container. Why do I have to use a different container type, GenericApplicationContext in this case? What if I want to read data into this object from the existing spring config in app.config or web.config? Would it work as the usual context? Could I then write data over these registrations with code?

  • How can I specify that ISomeService is to be created as a singleton? I don't mean supply a singleton instance to the container, but the container to create the instance, resolving its constructor, and use it when that type is needed.

  • how can I do the equivalent of container.RegisterType<ISomeService, ServiceImplementationA>(); ? I want to register type mappings to use in all cases where that type is needed by a constructor.

  • What exactly does container.RegisterType<ServiceImplementationA>("service"); do? It seems to register ServiceImplementationA as the implementation of ISomeService but ISomeServiceis never mentioned, so there could be ambiguity. e.g. what if ServiceImplementationA implemented more than one interface.

  • What is the string name given to the registration for? It won't work with en empty string, but it doesn't seem to matter what it is.

Am I trying to use spring in a way that it just does not work? I'm trying to use it like other IoC containers, but it's not quite working.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

提笔落墨 2024-08-23 03:49:34

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

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

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

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

我如何指定 ISomeService 是
被创建为单例?我不
意味着提供一个单例实例
容器,但容器
创建实例,解析其
构造函数,并在该类型时使用它
是需要的。

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

我怎样才能做相当于
容器.RegisterType(); ?我想要
要在所有中使用的寄存器类型映射
需要该类型的情况
构造函数。

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

具体是做什么的
容器.RegisterType(“服务”);
做?好像要注册
ServiceImplementationA 作为
ISomeService 的实现但是
ISomeService 从未被提及,所以
可能存在歧义。例如如果
ServiceImplementationA已实施
多个接口。

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

赋予的字符串名称是什么
注册为?它不能与
en 空字符串,但似乎不是
不管它是什么。

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

Adding as new answer trying to address the open points...

I want to integrate this technique
into existing code that uses the usual
container. Why do I have to use a
different container type,
GenericApplicationContext in this
case? What if I want to read data into
this object from the existing spring
config in app.config or web.config?
Would it work as the usual context?
Could I then write data over these
registrations with code?

Spring has concrete application context implementations for different kind of initialization tactics. The most common ones to use are GenericApplicationContext (manual), XmlApplicationContext (XML files) and WebApplicationContext (very much like XmlApplicationContext but tailored for web use). They all implement common interface: IApplicationContext which is the preferred way to access these containers.

Unfortonately altering registrations with code usually means that you need to use the specific sub-class directly. With GenericApplicationContext and StaticApplicationContext this is quite natural but XmlApplicationContext is usually considered to be XML only and this ways "fixed" to XML definition.

How can I specify that ISomeService is
to be created as a singleton? I don't
mean supply a singleton instance to
the container, but the container to
create the instance, resolving its
constructor, and use it when that type
is needed.

Your SpringHelper does just that, by default all objects in Spring are singletons. You could alter this behavior by calling ObjectDefinitionBuilder's SetSingleton method with false.

how can I do the equivalent of
container.RegisterType(); ? I want to
register type mappings to use in all
cases where that type is needed by a
constructor.

Spring uses object names (ids) to distinct between different implementations. So if you want to get specific type to serve a specific instance in case that there are many alternatives you should refer to this specific instance by name. If you are using autowiring and your object has dependency to interface ISomeService and there's only one object registered that implements it, the autowiring can set it without ambiguity.

What exactly does
container.RegisterType("service");
do? It seems to register
ServiceImplementationA as the
implementation of ISomeService but
ISomeServiceis never mentioned, so
there could be ambiguity. e.g. what if
ServiceImplementationA implemented
more than one interface.

Continuing from previous answer, this registers singleton of type ServiceImplementationA with name "service". This object comes autowiring candidate with all it's implemented interfaces (and with it's concrete type of course).

What is the string name given to the
registration for? It won't work with
en empty string, but it doesn't seem
to matter what it is.

It matters a great deal as explained earlier. The name is unique id within that context (parent context could have object with same name) and can be used to access specific object registrations. In short where other frameworks may associate a type as key to object registration, Spring uses name.

柒七 2024-08-23 03:49:34

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

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

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

That's a bit apples and oranges comparison as the unit test uses code configuration for Unity and XML (app.config) configuration for Spring.NET.

If you go the XML route, then you can either comment out old implementation A and define the B implementation as the one to use - that what's configuration is all about right? Other option is to have dedicated XML files for each scenario (configuration setup) and include them via context's resource definitions (you have inline resource now). Other options include file system and assembly, see the web configuration section in Spring.NET's manual for a nice example.

If you go the code configuration route I would suggest to check Spring.NET Recoil and upcoming CodeConfig.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文