tutorial que mvc mediante framework español desarrollo aplicaciones anotaciones java spring annotations factory-pattern

java - que - spring mvc 4 tutorial español



Implementar un patrón de fábrica simple con anotaciones de Spring 3 (7)

¿Por qué no agregar la interfaz FactoryBean a MyServiceFactory (para decirle a Spring que es una fábrica), agregar un registro (servicio String, instancia MyService) y hacer que cada uno de los servicios llame a:

@Autowired MyServiceFactory serviceFactory; @PostConstruct public void postConstruct() { serviceFactory.register(myName, this); }

De esta forma, puede separar a cada proveedor de servicios en módulos si es necesario, y Spring seleccionará automáticamente cualquier proveedor de servicios desplegado y disponible.

Me preguntaba cómo podría implementar el patrón simple de fábrica con las anotaciones de Spring 3. Vi en la documentación que puedes crear beans que llaman a la clase de fábrica y ejecutan un método de fábrica. Me preguntaba si esto era posible usando solo anotaciones.

Tengo un controlador que actualmente llama

MyService myService = myServiceFactory.getMyService(test); result = myService.checkStatus();

MyService es una interfaz con un método llamado checkStatus ().

Mi clase de fábrica se ve así:

@Component public class MyServiceFactory { public static MyService getMyService(String service) { MyService myService; service = service.toLowerCase(); if (service.equals("one")) { myService = new MyServiceOne(); } else if (service.equals("two")) { myService = new MyServiceTwo(); } else if (service.equals("three")) { myService = new MyServiceThree(); } else { myService = new MyServiceDefault(); } return myService; } }

La clase MyServiceOne se ve así:

@Autowired private LocationService locationService; public boolean checkStatus() { //do stuff }

Cuando ejecuto este código, la variable locationService es siempre nula. Creo que esto se debe a que estoy creando los objetos yo mismo dentro de la fábrica y no está teniendo lugar el cableado automático. ¿Hay alguna forma de agregar anotaciones para que esto funcione correctamente?

Gracias


Lo siguiente funcionó para mí:

La interfaz consiste en usted métodos lógicos más un método de identidad adicional:

public interface MyService { String getType(); void checkStatus(); }

Algunas implementaciones:

@Component public class MyServiceOne implements MyService { @Override public String getType() { return "one"; } @Override public void checkStatus() { // Your code } } @Component public class MyServiceTwo implements MyService { @Override public String getType() { return "two"; } @Override public void checkStatus() { // Your code } } @Component public class MyServiceThree implements MyService { @Override public String getType() { return "three"; } @Override public void checkStatus() { // Your code } }

Y la fábrica misma de la siguiente manera:

@Service public class MyServiceFactory { @Autowired private List<MyService> services; private static final Map<String, MyService> myServiceCache = new HashMap<>(); @PostConstruct public void initMyServiceCache() { for(MyService service : services) { myServiceCache.put(service.getType(), service); } } public static MyService getService(String type) { MyService service = myServiceCache.get(type); if(service == null) throw new RuntimeException("Unknown service type: " + type); return service; } }

Encontré tal implementación más fácil, más limpia y mucho más extensible. Agregar un nuevo MyService es tan fácil como crear otro bean de primavera que implemente la misma interfaz sin realizar ningún cambio en otros lugares.


Puede instanciar "AnnotationConfigApplicationContext" pasando todas sus clases de servicio como parámetros.

@Component public class MyServiceFactory { private ApplicationContext applicationContext; public MyServiceFactory() { applicationContext = new AnnotationConfigApplicationContext( MyServiceOne.class, MyServiceTwo.class, MyServiceThree.class, MyServiceDefault.class, LocationService.class ); /* I have added LocationService.class because this component is also autowired */ } public MyService getMyService(String service) { if ("one".equalsIgnoreCase(service)) { return applicationContext.getBean(MyServiceOne.class); } if ("two".equalsIgnoreCase(service)) { return applicationContext.getBean(MyServiceTwo.class); } if ("three".equalsIgnoreCase(service)) { return applicationContext.getBean(MyServiceThree.class); } return applicationContext.getBean(MyServiceDefault.class); } }


Puede solicitar manualmente a Spring que lo autocance.

Haga que su fábrica implemente ApplicationContextAware. Luego proporcione la siguiente implementación en su fábrica:

@Override public void setApplicationContext(final ApplicationContext applicationContext { this.applicationContext = applicationContext; }

y luego haz lo siguiente después de crear tu bean:

YourBean bean = new YourBean(); applicationContext.getAutowireCapableBeanFactory().autowireBean(bean); bean.init(); //If it has an init() method.

Esto conectará automáticamente su LocationService perfectamente.


Supongo que debes usar org.springframework.beans.factory.config.ServiceLocatorFactoryBean. Simplificará mucho su código. Excepto MyServiceAdapter, solo puede crear la interfaz MyServiceAdapter con el método MyService getMyService y con alies para registrar sus clases.

Código

bean id="printStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"> <property name="YourInterface" value="factory.MyServiceAdapter" /> </bean> <alias name="myServiceOne" alias="one" /> <alias name="myServiceTwo" alias="two" />



Tienes razón, al crear objetos manualmente, no dejas que Spring realice el autoencendido. Considere administrar sus servicios por Spring también:

@Component public class MyServiceFactory { @Autowired private MyServiceOne myServiceOne; @Autowired private MyServiceTwo myServiceTwo; @Autowired private MyServiceThree myServiceThree; @Autowired private MyServiceDefault myServiceDefault; public static MyService getMyService(String service) { service = service.toLowerCase(); if (service.equals("one")) { return myServiceOne; } else if (service.equals("two")) { return myServiceTwo; } else if (service.equals("three")) { return myServiceThree; } else { return myServiceDefault; } } }

Pero consideraría que el diseño general es bastante pobre. ¿No sería mejor tener una implementación general de MyService y pasar MyService one / two / three como parámetro extra para checkStatus() ? ¿Qué quieres lograr?

@Component public class MyServiceAdapter implements MyService { @Autowired private MyServiceOne myServiceOne; @Autowired private MyServiceTwo myServiceTwo; @Autowired private MyServiceThree myServiceThree; @Autowired private MyServiceDefault myServiceDefault; public boolean checkStatus(String service) { service = service.toLowerCase(); if (service.equals("one")) { return myServiceOne.checkStatus(); } else if (service.equals("two")) { return myServiceTwo.checkStatus(); } else if (service.equals("three")) { return myServiceThree.checkStatus(); } else { return myServiceDefault.checkStatus(); } } }

Esto todavía está mal diseñado porque agregar nueva implementación de MyServiceAdapter requiere la modificación MyServiceAdapter (violación de SRP). Pero este es en realidad un buen punto de partida (sugerencia: mapa y patrón de Estrategia).