对于我的应用程序,我有一个Scale
接口和多个实现此接口的类,例如NormalizedScale
,LogScale
等。在我的一个服务中,我需要创建许多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()
。
答案 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>
自动装入,然后将其转换为Map
(ScaleFactory
} 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容器。