8

我正在使用 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) {
    }
}

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

4

4 回答 4

11

假设您在生产环境中需要这两种实现(如果不需要 - 使用 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;
         }
      }
   }
}
于 2013-08-07T05:51:28.427 回答
3

您的问题意味着“动态”意味着您希望能够在启动时进行选择,但您不需要能够在运行中更改它。如果是这种情况,我强烈建议使用@Profile. 我通常的做法是使用基于注释的配置,为每个配置文件创建一个单独的@Configuration类,该类定义@Bean需要在其中可用的配置文件特定的 s。甚至可以轻松定义@Controller在生产模式下完全禁用的 development-only 。为每个配置文件定义一个带有String常量的类以避免拼写错误。

于 2013-08-07T00:36:24.353 回答
1

这是未经测试的,但如果您使用的是 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
    ) {
        ...
    }

}

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

于 2013-08-06T23:42:35.707 回答
0

您可以使用@Named注解,并按名称获取 bean。

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

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

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

于 2013-08-06T23:29:22.120 回答