我们可以有一个工厂类作为spring bean,并有一个工厂方法根据条件返回多个spring bean吗?
Posted
技术标签:
【中文标题】我们可以有一个工厂类作为spring bean,并有一个工厂方法根据条件返回多个spring bean吗?【英文标题】:Can we have a factory class as spring bean and have a factory method returning multiple spring beans based on the condition? 【发布时间】:2019-09-02 03:16:57 【问题描述】:我想根据工厂类中的条件返回多个spring bean。
这是一个好习惯吗?
有没有更好的方法来编写以下代码? 还有其他适合这里的设计模式吗?
下面是sn-p的代码:
package com.test;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class InstanceFactory
@Resource(name = "instance1")
private Instance instance1;
@Resource(name = "instance2")
private Instance instance2;
public Instance getService(Condition condition)
if (condition.one() && condition.two())
return instance2;
else
return instance1;
【问题讨论】:
对我来说看起来非常好。虽然这可能取决于您的实际情况。 这看起来像是codereview.stackexchange.com的问题 你知道FactoryBeans吗?或者根据情况,配置文件可能有用。为了有一个明确的答案,应该有更多的细节。 感谢 @biziclop 和 Nathan Hughes。我将研究 FactoryBeans 【参考方案1】:这取决于您想要达到的目标。工厂模式旨在创建对象,但您返回的是已经在其他地方创建的对象(在本例中为 Spring)。如果你想创建将由 Spring 管理的 bean,有几种方法:
@Conditional(YourConditionImplementation.class):在@Configuration 注释类的方法上添加的此注释将允许您在满足给定条件时创建一个bean。此处示例:https://javapapers.com/spring/spring-conditional-annotation/
您也可以使用 BeanFactory 将 bean 的定义 (DefinitionBean) 注入到容器中。此处示例:https://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition.html
现在,如果您想要一个对象来确定 Instance 类型的对象更适合某些需求,那么您的方法是可以的,但从技术上讲它不是工厂 :)
【讨论】:
不建议在spring boot自动配置库等中使用@ConditionOnMissingBean 根据spring doc推荐:docs.spring.io/spring-boot/docs/current/reference/html/…。无论如何,没有人对弹簧靴说任何话。目前只有 spring 和带有自定义条件类的 @conditional。【参考方案2】:在设计类似的东西时,我会考虑两种设计模式来面对该解决方案:
策略模式:为了在每次需要评估更多实例时替换重复的if else。 装饰器模式:尝试使每个条件尽可能可配置。它们可以为一个或多个谓词组合(修饰)。考虑到这两种模式,您可能会实现以下目标:
首先,定义哪些条件将识别给定实例:
public enum InstanceType
INSTANCE_TYPE_1(Condition::isOne, Condition::isTwo),
INSTANCE_TYPE_2(Condition::isOne, Condition::isThree),
...;
private List<Predicate<Condition>> evaluators;
@SafeVarargs
InstanceType(final Predicate<Condition>... evaluators)
this.evaluators = Arrays.asList(evaluators);
public boolean evaluate(final Condition condition)
return evaluators.stream().allMatch(it -> it.test(condition));
然后,您应该将每个实例实现链接到特定的实例类型:
@Component
public class InstanceOne implements Instance
@Override
public InstanceType getType()
return InstanceType.INSTANCE_TYPE_1;
最后,配置一个类,将类型和实例之间的关系定义为 EnumMap
@Configuration
public class InstanceFactoryConfig
@Autowired
private List<Instance> instances;
@Bean
public EnumMap<InstanceType, Instance> instancesMap()
EnumMap<InstanceType, Instance> instanceEnumMap = new EnumMap<>(InstanceType.class);
instances.forEach(i -> instanceEnumMap.put(i.getType(), i));
return instanceEnumMap;
因此,您可以将 InstanceFactory 替换为如下内容:
public class InstanceFactory
@Autowire
private final EnumMap<InstanceType, Instance> instancesMap;
public void getInstance(Condition condition)
instancesMap.get(getInstanceType(condition)).doSomething();
private InstanceType getInstanceType(Condition condition)
return Arrays.stream(InstancesType.values())
.filter(evaluator -> evaluator.evaluate(condition))
.findFirst().orElseThrow(() -> new RuntimeException("Instance type not found"));
如您所见,您的 InstanceFactory 不太容易被修改。这意味着,每次您需要添加新的实例实现时,您只需要修改 InstanceType 枚举。希望这会有所帮助。
【讨论】:
【参考方案3】:您可以使用spring现有的FactoryBean接口并实现自己的逻辑 这是在 Spring 框架中创建 bean 的最佳方法之一 这是示例的链接:
https://www.baeldung.com/spring-factorybean
【讨论】:
【参考方案4】:见: Spring Profile
活动配置文件由属性设置,并且根据您分配给配置文件的值,Spring 将为同一接口加载不同的 bean。 所以它可能正是你所需要的。
【讨论】:
以上是关于我们可以有一个工厂类作为spring bean,并有一个工厂方法根据条件返回多个spring bean吗?的主要内容,如果未能解决你的问题,请参考以下文章