如何在 Spring.Net 中更改配置

Posted

技术标签:

【中文标题】如何在 Spring.Net 中更改配置【英文标题】:How to change configs in Spring.Net 【发布时间】:2010-12-29 16:15:04 【问题描述】:

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>

更新

这是基于the links that Marko Lahma gave to Mark Pollack's blog 代码的部分答案。我通过了上述测试,代码如下:

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

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

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

我是否试图以一种无法正常工作的方式使用弹簧?我正在尝试像使用其他 IoC 容器一样使用它,但效果不佳。

【问题讨论】:

【参考方案1】:

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

我想整合这项技术 到使用通常的现有代码中 容器。为什么我必须使用 不同的容器类型, GenericApplicationContext 在这 案子?如果我想将数据读入 这个对象来自现有的弹簧 在 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 使用对象名称(id)来区分不同的实现。因此,如果您想获得特定类型以服务特定实例,以防有许多替代方案,您应该按名称引用此特定实例。如果您正在使用自动装配并且您的对象依赖于接口 ISomeService 并且只有一个注册的对象实现了它,那么自动装配可以毫无歧义地设置它。

具体是做什么的 container.RegisterType("服务"); 做?好像要注册 ServiceImplementationA 作为 ISomeService 的实现但是 ISomeService 从未被提及,所以 可能有歧义。例如如果 ServiceImplementationA已实施 多个界面。

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

给定的字符串名称是什么 注册?它不适用于 en 空字符串,但似乎没有 不管它是什么。

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

【讨论】:

混合XML和代码配置很重要,可惜不容易做到。注册名称与什么匹配?假设我注册了两个 ISomeService 实现 - 哪个插入到自动装配的构造函数中 - 是注册名称与参数名称匹配的那个吗? 相关,可以做new GenericApplicationContext(IApplicationContext parent)。这是否允许您将 XmlApplicationContext 作为父级,并使用两者有效地解析类 - 如果存在代码配置,则回退到父 XML 配置? 更新:使用“new GenericApplicationContext(ContextRegistry.GetContext())”的简单测试表明您无法在两者中都使用注册来解析类。那么父上下文是什么? Spring.NET 有不同的自动装配模式,在这里描述:springframework.net/doc/reference/html/…。所以 0 或大于 1 是错误条件。 父上下文允许例如框架级别和模块级别之间的分离。您可以在根上下文中托管您的公共共享服务,并为插件之类的东西创建子上下文。然后插件将从它们指定的容器或父容器中找到它们的依赖项。子容器定义可以通过使用它们的定义更接近可见性来“覆盖”父容器的定义。【参考方案2】:

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

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

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

【讨论】:

关于“苹果和橙子的比较”,但我尝试根据其惯常做法和可用示例来使用每个工具。所以它在代码中使用 config 来实现统一,在 app.config 中使用 config 来实现 spring。 unity 和 spring 实际上涵盖相同的场景吗?还是那是“苹果与橘子”?建议将 A 配置注释掉以使 B 工作不是一个好主意。这例如用 mocks 模拟测试用例。每个测试可能需要不同的模拟,并且所有测试都必须一次性通过。这种情况对我们和其他许多人来说都是关键。 我被引导相信 spring 根本无法在代码中进行配置。感谢您提供的链接。这些附加组件是否成熟?我还希望看到您提到的配置设置方法的工作示例。虽然看起来可能,但我发现的文档并不清楚究竟要在这里做什么。在大多数其他 IoC 容器中它是单行的,在 Spring 中是什么样的? 这个权威答案:***.com/questions/411660/… 说 Spring.Net 仅在 XML 中配置。 是的,XML 是配置 Spring.NET 的常用方法。始终存在由普通 .NET 类组成的底层配置元模型。 Mark Pollack 在这里有一篇很好的博客文章:blog.springsource.com/2008/01/04/…。 Spring.NET Recoil 是第 3 方的工作,是配置模型之上的扩展(就像 XML)。 CodeConfig 正在进行中,由 Spring.NET 核心开发人员制作。 用“苹果和橘子”来表示不同方法的基本问题。测试用例的代码配置更容易,当有相当静态的模型(或无需修改即可组合的 XML 场景文件)时,XML 更适合。

以上是关于如何在 Spring.Net 中更改配置的主要内容,如果未能解决你的问题,请参考以下文章

sprin boot 配置文件的获取顺序

Castle IOC容器与Spring.NET配置之比较

如何在 Spring.Net 应用程序上下文中添加 ASP.NET MVC 控制器?

Spring.NET配置

Spring.Net学习笔记-设置配置文件参数

如何在Maven中配置Spring依赖