用Spring配置抽象工厂的实现类

时间:2015-12-25 19:51:50

标签: java spring

对于我的应用程序,我有一个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中使用autowired属性来定义scaleImplClass。但这似乎很奇怪,因为我的工厂也是一个服务,我有一个该工厂的实例。

-

另一种方法是在我的服务中使用Class scaleImplementationClass属性而不是ScaleFacotry并使用ScaleFactory,如下所示:

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

...

Scale myScale = ScaleFactory.getScale(scaleImplementationClass);

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

3 个答案:

答案 0 :(得分:3)

有几种不同的类似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<ScaleFactory>自动装入,然后将其转换为MapScaleFactory } type是关键)。这可以避免您需要担心Scale的类名,并使您能够在以后更改它们(只要保持type键一致)`

您的ScaleFactory实现可以随心所欲地执行任何操作。例如,如果您知道某种类型的Scale是不可变的,则可以让工厂每次都返回相同的实例。或者,您可以让每个调用返回一个单独的实例 - Scale的实例化取决于依赖于实现的工厂。

答案 1 :(得分:0)

您可以简单地使用&#34;限定符&#34;这基本上是指一个特定的&#34;命名&#34;豆。默认情况下,bean名称是类的名称,小写的第一个字母(MyClass - &gt; myClass)。如果您想定义自己的名字,可以按照以下步骤操作:

@Service("customizedBeanName")

你最终会做这样的事情:

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

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

答案 2 :(得分:0)

对于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容器。