简体   繁体   English

基于运行时条件的Spring开关实现

[英]Spring switch implementations based on a runtime condition

This is a simplified version of what I am trying to achieve. 这是我想要实现的简化版本。 I have multiple implementations of the same interface. 我有相同接口的多个实现。 Based on the user input at runtime I want to pick the correct implementation. 根据运行时的用户输入,我想选择正确的实现。

For example suppose I an interface called Color. 例如,假设我是一个名为Color的接口。 There are many classes that implement this interface, the Red class, the Blue class, the Green class and so on. 有许多实现此接口的类,Red类,Blue类,Green类等。

At run time I need to pick implementations based on the user input. 在运行时,我需要根据用户输入选择实现。 One way to achieve this would be something like this 实现这一目标的一种方法是这样的

 @Autowired
 @Qualifier("Red")
 private Color redColor;

 @Autowired
 @Qualifier("Green")
 private Color greenColor;


private Color getColorImplementation()
{
if(userInput=="red")
{
return redColor;

}
else if(userInput=="green")
{
return greenColor;
}
else
{
return null;
}

}

But the problem with this is that everytime a new implementation is added, I would have to update the code that picks the implementation, which beats the whole purpose of inversion of control part of spring. 但问题是,每次添加新实现时,我都必须更新选择实现的代码,这就是弹簧控制部分反转的全部目的。 What is the right way to do this using spring? 用弹簧做这件事的正确方法是什么?

You could autowire all implementations of the interface in question and then decide based on properties provided by interface which to use. 您可以自动装配相关接口的所有实现,然后根据接口提供的属性来决定使用。

@Autowired
private List<Color> colors;

public void doSomething(String input) {
    colors.stream().filter(c -> c.getName().contains(input)).findFirst().ifPresent(c -> {
        // something
    }
}

This is also less magical and more in line with OO principles. 这也不那么神奇,更符合OO原则。 Dependency injection is to wire up things initially, not for dynamic switching at runtime. 依赖注入最初是为了连接事物,而不是在运行时进行动态切换。

You want to Autowire the ApplicationContext , then you can get all the Color beans with Map<String, Color> colors = appContext.getBeansOfType(Color.class); 您想要自动装配ApplicationContext ,然后您可以使用Map<String, Color> colors = appContext.getBeansOfType(Color.class);获取所有Color bean Map<String, Color> colors = appContext.getBeansOfType(Color.class); . This presumes that the userInput and the bean name are identical. 这假设userInput和bean名称相同。

If that isn't the case, a solution would be to add a getName() to the Color interface; 如果不是这种情况,解决方案是将一个getName()添加到Color接口; then you can autowire a List<Color> and construct the Map yourself. 然后你可以自动装配List<Color>并自己构建Map。

Can't you make the Color an Enum? 难道你不能把颜色变成一个枚举?

The Spring ServiceLocatorFactoryBean (scroll down to the middle) API was built just for this purpose: Spring ServiceLocatorFactoryBean (向下滚动到中间)API就是为此目的而构建的:

  1. Create a dummy interface ( ColorFactory ) that provides a single method such as Color getColor(String color) 创建一个虚拟接口( ColorFactory ),提供单个方法,如Color getColor(String color)
  2. Create the proxy bean instance for org.springframework.beans.factory.config.ServiceLocatorFactoryBean passing ColorFactory as the serviceLocatorInterface parameter org.springframework.beans.factory.config.ServiceLocatorFactoryBean创建代理bean实例,将ColorFactory作为serviceLocatorInterface参数传递
  3. Define beans for all of your color implementations with names matching the parameter you'd like to pass to getColor 为所有颜色实现定义bean,其名称与您要传递给getColor的参数相匹配
  4. Inject the factory into the collaborators and invoke getColor as needed 将工厂注入协作者并根据需要调用getColor

You could contrive this with similar APIs on the ApplicationContext , but the advantage of this approach is that it abstracts Spring from your Java implementation (for XML configured projects). 您可以在ApplicationContext上使用类似的API来设计它,但这种方法的优点是它从Java实现中抽象出Spring(对于XML配置的项目)。

Same issue happen in my implementation where in, the scenario was based on user input, where the respective interface implementation needs to be invoked. 同样的问题发生在我的实现中,其中,场景基于用户输入,其中需要调用相应的接口实现。

This solve my problem: 这解决了我的问题:

        **Base Interface**
        @Service
        public interface ParentInterface {
                public String doThis(ClassA param);
        }

        **First Implementation**

        @Component("FirstImp")
        public class FirstServiceImp implements ParentInterface {
        public String doThis(ClassA param){

        }
        **Second Implementation**

        @Component("SecondImp")
        public class SecondServiceImp implements ParentInterface {
        public String doThis(ClassA param){

        }

        **Factory**
        @Service
        public class ServiceResolver {

        @Autowired
        @Qualifier("FirstImp")
        private ParentInterface firstImpl;

        @Autowired
        @Qualifier("SecondImp")
        private ParentInterface secondImpl;

        public ParentInterface getInstance(String condition){
            switch(condition) {
            case "X": return firstImpl;
            case "Y": return secondImpl;
            default:
                throw new IllegalArgumentException(condition);
        }
        }
        }
        **Controller**
        @RestController
        public class UserController {
        @Resource
        private ServiceResolver serviceresolver;

        @PostMapping("/userbase/{inp1}/messages/{inptype}")
        public ResponseEntity<String> sendData(@PathVariable String 
        inp1,@PathVariable String inptype, @RequestBody XYZBean msg) 
        {
        for(ABC data : msg.getSubData())
            serviceresolver.getInstance(data.getType()).doThis(msg);
         return new ResponseEntity<String>("created",HttpStatus.OK);
        }

        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM