【问题标题】:Dyamically choose service implementation in Spring在 Spring 中动态选择服务实现
【发布时间】:2013-08-08 03:46:43
【问题描述】:

我正在使用 spring 3.2,并希望根据条件在我的控制器中动态选择服务实现。考虑我有一个接口和两个实现如下:

public interface DevService {
   public void add(Device device);
}

public class DevServiceImpl implements DevService {
    public void add(Device device) {
    }
}

public class RemoteDevServiceImpl implements DevService {
    public void add(Device device) {
    }
}

所以在我的控制器中,根据操作是在本地站点还是远程站点上执行,我需要在本地执行它或者向远程站点发送命令来执行它。本质上,用户点击的站点决定了调用哪个服务实现。任何人都可以提出一种干净的方法来实现这一目标吗?

【问题讨论】:

    标签: java spring spring-mvc dependency-injection


    【解决方案1】:

    假设您在生产环境中需要这两种实现(如果不需要 - 使用 Spring 配置文件在环境之间清楚地拆分 bean)。简单的方法是:

    interface DevService
    {
       void add(Device d);
       String getName();
    }
    
    @Service("devServiceLocal")
    class DevServiceLocalImpl implements DevService
    {
       void add(Device d) {...}
       String getName() {return "local";}
    }
    
    class Controller
    {
       @Autowired
       Collection<DevService> services;
    
       void doSomethingWithService()
       {
          // TODO: Check type somehow
          String servType = "local";
          for(DevService s: services)
          {
             if(servType.equals(s.getName())
             {
                // Call service methods
                break;
             }
          }
       }
    }
    

    【讨论】:

      【解决方案2】:

      您的问题暗示“动态”是指您希望能够在启动时进行选择,但您不需要能够在运行中进行更改。如果是这种情况,我强烈建议使用@Profile。我通常的做法是使用基于注释的配置,为每个配置文件创建一个单独的 @Configuration 类,该类定义需要在其中可用的配置文件特定的@Beans。定义在生产模式下完全禁用的纯开发@Controllers 甚至很容易。为每个配置文件定义一个带有String 常量的类以避免拼写错误。

      【讨论】:

        【解决方案3】:

        这是未经测试的,但是如果您使用的是 Spring-MVC,您应该能够通过检查控制器中的标头来处理它。不过,我不确定您的代码是如何部署的。以下方法有一些缺点,但应该可行:

        package <PACKAGE NAME>;
        
        import org.springframework.stereotype.Controller;
        import org.springframework.web.bind.annotation.ModelAttribute;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;
        
        @Controller
        @RequestMapping("/device")
        public class DeviceController {
        
            @RequestMapping(headers={"Host=*.local.domain.com"})
            public void localDevice(
                @ModelAttribute("device") Device device
            ) {
                ...
            }
        
            @RequestMapping(headers={"Host!=*.local.domain.com"})
            public void remoteDevice(
                @ModelAttribute("device") Device device
            ) {
                ...
            }
        
        }
        

        也就是说,它依赖于您的主机保持不变,虽然您可以为您的标头注入一个列表,但我不确定您是否仍然可以使用该方法获取远程主机的排除映射。另外,我认为如何将请求路由到控制器类非常重要。

        【讨论】:

          【解决方案4】:

          你可以使用@Named注解,通过名字获取bean。

          这样,您可以定义一个基本名称“ServiceName”并添加一些后缀,如“Remote”或“Local”,具体取决于您解释的条件。您还需要注释您的 bean(服务实现),例如

          @Named("ServiceNameRemote")@Named("ServiceNameLocal") 分别

          最后在您的控制器上,您可以使用(DevService)BeanFactory.getBean("beanName") 通过其动态生成的名称来获取它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-09
            • 1970-01-01
            • 2012-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-29
            相关资源
            最近更新 更多