Elija el servicio de implementación en spring.

Estoy usando Spring 3.2 y me gustaría elegir dinámicamente una implementación de servicio en mi controlador dependiendo de una condición. Considera que tengo una interfaz y dos implementaciones de la siguiente manera:

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) { } } 

Entonces, en mi controlador, dependiendo de si la acción se va a ejecutar en el sitio local o en el sitio remoto, necesito ejecutarlo localmente o enviar un comando al sitio remoto para ejecutarlo. Esencialmente, el sitio en el que el usuario hace clic determina a qué servicio implícito llamar. ¿Alguien puede sugerir una manera limpia de lograr esto?

Suponiendo que necesita ambas implementaciones en el entorno de producción (de lo contrario, use los perfiles Spring para dividir claramente los beans entre los entornos). El enfoque simple sería:

 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 services; void doSomethingWithService() { // TODO: Check type somehow String servType = "local"; for(DevService s: services) { if(servType.equals(s.getName()) { // Call service methods break; } } } } 

Su pregunta implica que con “dinámicamente” quiere decir que le gustaría poder seleccionar en el momento del inicio, pero que no necesita poder cambiarlo en el momento. Si ese es el caso, recomiendo usar @Profile . Mi práctica habitual, al utilizar una configuración basada en anotaciones, es tener una clase de @Configuration para cada perfil que defina los @Bean específicos del perfil que deben estar disponibles allí. Incluso es fácil definir @Controller s de desarrollo que están completamente deshabilitados en el modo de producción. Defina una clase con constantes de String para cada perfil para evitar errores tipográficos.

Puede usar la anotación @Named y obtener el bean por nombre.

De esta manera, puede definir un nombre base “ServiceName” y agregar algunos sufijos, como “Remoto” o “Local”, según la condición que haya explicado. También necesitará anotar sus beans (implementaciones de servicio) como

@Named("ServiceNameRemote") y @Named("ServiceNameLocal") respectivamente

Finalmente, en su controlador, puede usar (DevService)BeanFactory.getBean("beanName") para obtenerlo por su nombre generado dinámicamente.

Esto no se ha probado, pero si está utilizando Spring-MVC, debería poder manejarlo revisando sus encabezados en el controlador. Sin embargo, no estoy seguro de cómo se está implementando su código. El siguiente enfoque tiene algunas desventajas, pero debería funcionar:

 package ; 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 ) { ... } } 

Es decir, depende de que su host permanezca igual, mientras que puede insertar una lista para sus encabezados, no estoy seguro de que aún pueda obtener el mapa de exclusión para el host remoto utilizando ese enfoque. Además, creo que importa mucho cómo se enruta la solicitud a la clase de controlador.