【问题标题】:Configure Implementation Class of Abstract Factory with Spring用Spring配置抽象工厂的实现类
【发布时间】:2016-03-31 16:08:18
【问题描述】:

对于我的应用程序,我有一个Scale 接口和多个实现该接口的类,例如NormalizedScaleLogScale 等。在我的一个服务中,我需要创建许多Scales,我想使用 Spring 来定义它应该创建的 Scale 的实现。我将如何实现这样的东西?

--

我正在考虑创建一个工厂 ScaleFactory,就像在抽象工厂模式中一样,我可以调用 ScaleFactory.getScale() 来获得我在 Spring XML 中配置的任何实现的规模:

class ScaleFactory {
    Class<? extends Scale> scaleImplClass;
    public static Scale getScale() {
        return scaleImplClass.newInstance();
    }
}


Scale myScale = ScaleFactory.getScale();

但是使用这种方法,我如何配置 ScaleFactory 应该从 Spring XML 中使用哪个实现?

--

另一种方法是将ScaleFactory 设为@Service,然后将ScaleFactory 自动连接到我的服务中:

@Autowired
ScaleFactory scaleFactory;

...

Scale myScale = scaleFactory.getScale();

然后我可以使用 ScaleFactory 中的自动装配属性来定义 scaleImplClass。但这似乎很奇怪,因为我的工厂也是一个服务,我有那个工厂的一个实例。

--

另一种方法是在我的 service 中使用 Class scaleImplementationClass 属性而不是 ScaleFacotry 并像这样使用 ScaleFactory:

@Value("${scaleImplementationClass}")
Class scaleImplementationClass

...

Scale myScale = ScaleFactory.getScale(scaleImplementationClass);

但是工厂是毫无意义的,因为我也可以运行scaleImplementationClass.newInstance()

【问题讨论】:

  • 您希望通过这种方法获得什么?抽象工厂实际上正在取代 Spring 想要为您做的一些事情,并远离依赖注入,这是您感到阻力的地方。相反,您可以做的只是将@Autowire private Scale scale 添加到需要Scale 实例的任何类中。 Spring 将自动找到您在 XML 中定义的任何实现并将其加载到相应位置。你不需要工厂类。
  • Colin,如果我 @Autowire Scale scale,我只会得到 一个 比例,但我需要创建 许多 比例。这就是为什么我认为我需要一个工厂。我猜另一种选择是创建多个工厂(每个实现 Scale 一个),然后 @Autowire 正确的工厂。
  • 这是有道理的。请参阅下面的答案,如果它对您有帮助,请告诉我。 (编辑:删除了我的答案,意识到它没有直接回答你的问题。清除它)。
  • 添加了我更新的答案

标签: java spring


【解决方案1】:

您可以简单地使用“限定符”,它基本上将指向特定的“命名”bean。默认情况下,bean 名称是类的名称,第一个字母小写(MyClass -> myClass)。如果你想定义自己的名字,你可以这样做:

@Service("customizedBeanName")

你最终会做这样的事情:

@Autowired
@Qualifier("logScale")
private Scale logScale;

@Autowired
@Qualifier("anotherScale")
private Scale anotherScale;

【讨论】:

【解决方案2】:

有几种不同的类似 Spring 的方法可以处理这个问题。我个人采用的方法看起来有点像这样:

public interface ScaleFactory {

    public Scale newInstance();
    public String type();

}

public class FirstScaleFactory implements ScaleFactory {

    public Scale newInstance() {
        return new FirstScale();
    }

    public String type() {
        return "first";
    }    

}

public class SecondScaleFactory implements ScaleFactory {

    public Scale newInstance() {
        return new SecondScale();
    }

    public String type() {
        return "second";
    }    

}

public class ScaleManager {

    private final Map<String, ScaleFactory> factories;

    @Autowired
    public ScaleManager(List<ScaleFactory> factories) {
        this.factories = factories.stream()
            .collect(Collectors.toMap(f -> f.type(), Function::identity));
    }

    public Scale newInstance(String type) {
        return Optional.ofNullable(factories.get(type))
            .map(factory -> factory.newInstance())
            .orElseThrow(IllegalArgumentException::new);
    }

}

通过这种方法,您的ScaleManager 是一个标准的 Spring bean,可以连接到任何需要规模实例的类中。在初始化时,它获取 Spring 上下文中定义的所有 ScaleFactories,并将它们自动连接为 List&lt;ScaleFactory&gt;,然后将其转换为 Map(其中 ScaleFactory 类型是键)。这避免了您需要担心Scale 的类名,并让您能够在以后更改它们(只要您保持type 键一致)`

然后您的ScaleFactory 实现可以做任何他们需要做的事情。例如,如果您有一种类型的 Scale,您知道它是不可变的,您可以让工厂每次都返回相同的实例。或者,您可以让每个调用都返回一个单独的实例 - Scale 的实例化取决于依赖于实现的工厂。

【讨论】:

    【解决方案3】:

    对于 spring 5.x,有一种更简单、更干净的方法。我已决定使用@ConditionalOnProperty 注释,但您可以选择任何您喜欢的@Conditional*。

    事情是这样的,我已经简化到了极致:

    public interface MyService {}
    
    @Service
    @ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Some")
    public class SomeService implements MyService {}
    
    @Service
    @ConditionalOnProperty(prefix = "myService", name = "Impl", havingValue = "Foo")
    public class FooService implements MyService {}
    
    @Service
    public class SimpleService {
    
      @Autowired
      SimpleService(MyService service) {
        // service instance will depend on configuration
      }
    }
    

    我正在使用 springboot,所以我决定使用application.properties 来通过这样的环境变量设置值:

    myService.Impl=${MY_SERVICE_IMPL}
    

    然后,我有一个基于环境变量的完全动态注入,例如可以传递给 docker 容器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-27
      相关资源
      最近更新 更多