29 Temmuz 2021 Perşembe

SpringContext @Qualifier Anotasyonu Alternatifi - Strategy Pattern

Örnek - Generic Bean
Bir örnek burada
Bir örnek burada

Örnek - application.properties
Bu yöntemde iki tane bean yaratılıyor. Hangisinin inject edileceği application.properties ile seçilebiliyor.

Elimizde şöyle bir anotasyon olsun
package your.package;

@Retention(RetentionPolicy.RUNTIME)
public @interface InjectDynamicObject {
}
İki tane bean yaratalım
public interface Customer {
  public String getName();
}

@Component("customerOne")
public class CustomerOneImpl implements Customer {
  @Override
  public String getName() {
    return "Customer One";
  }
}

@Component("customerTwo")
public class CustomerTwoImpl implements Customer {
  @Override
  public String getName() {
    return "Customer Two";
  }
}
Çözüm 1 - Kolay Olan
Açıklaması şöyle
Spring injects the Map with Bean name as String and the Bean itself automagically.
Şöyle yaparız
public class CustomerServiceImpl {
    
  // We inject the customer implementations into a Map
  @Autowired
  private Map<String, Customer> dynamicCustomerWithMap;
    
  // This comes from the property file as a key for the Map
  @Value("${dynamic.object.name}")
  private String object;
  public Customer getDynamicCustomerWithMap() {
    return this.dynamicCustomerWithMap.get(object);
  }
}
Çözüm 2 - Zor Olan
Bu anotasyonu bir metodda kullanalım ve arayüzümüzü dönelim. Bu servis sınıfı @Autowire edilirse, aşağıda tanımlım olan @Around çalışır ve application.properties dosyasındaki tanımlı bean döner.
@Service
public class CustomerServiceImpl {
  private Customer dynamicCustomerWithAspect;
    
  @InjectDynamicObject
  public Customer getDynamicCustomerWithAspect() {
    return this.dynamicCustomerWithAspect;
  }
}
Bu metod için bir Aspect yazalım. 
@Component
@Aspect
public class DynamicObjectAspect {
  // This comes from the property file
  @Value("${dynamic.object.name}")
  private String object;
  @Autowired
  private ApplicationContext applicationContext;
    
  @Pointcut("execution(@com.lofi.springbean.dynamic.InjectDynamicObject * *(..))")
  public void beanAnnotatedWithInjectDynamicObject() {
  }
  @Around("beanAnnotatedWithInjectDynamicObject()")
  public Object adviceBeanAnnotatedWithInjectDynamicObject(ProceedingJoinPoint pjp)
throws Throwable {   
    pjp.proceed();
        
    // Create the bean or object depends on the property file  
    Object createdObject = applicationContext.getBean(object);
    return createdObject;
  }
}
Örnek - Metod Parametresi
Elimizde şöyle bir arayüz olsun
public enum GeolocationProvider {
  GOOGLE_MAP,
  GEO_BOUNDARIES
}

public interface GeolocationService {

  // to identify support for the provider
  GeolocationProvider getGeoProvider();

  //method will have the provider specific logic to find the latitude/longitude
  Result getLatLongByCity(String city);
}
Bu arayüzü gerçekleştiren iki bean olsun
@Service
public class GoogleMapLocationServiceImpl implements GeolocationService {

  @Override
  public GeolocationProvider getGeoProvider() {
    return GeolocationProvider.GOOGLE_MAP;
  }

  @Override
  public Result getLatLongByCity(String city) {
     ...
  }
}
@Service
public class GeoBoundariesLocationServiceImpl implements GeolocationService {


  @Override
  public GeolocationProvider getGeoProvider() {
    return GeolocationProvider.GEO_BOUNDARIES;
  }

  @Override
  public Result getLatLongByCity(String city) {
    ...
  }
}
Bu iki bean arasında seçim yapmak için metoda parametre geçeriz
@Component
public class GeoLocationProviderContext {

  private final EnumMap<GeolocationProvider, GeolocationService> map;

  @Autowired
  public GeoLocationProviderContext(Set<GeolocationService> geolocationServiceSet) {
    map = new EnumMap<>(GeolocationProvider.class);
    geolocationServiceSet.forEach(service -> map.put(service.getGeoProvider(), service));
  }
 
  public GeolocationService getService(GeolocationProvider provider) {
    return map.get(provider);
  }
}

@RestController
public class GeolocationController {

  @Autowired
  GeoLocationProviderContext geoLocationProviderContext;

  @GetMapping(value = "/coordinates/for/city/{cityname}/{provider}")
  public ResponseEntity<Result> getLocation(@PathParam(value = "cityname") String city,
                          @PathParam(value = "provider")GeolocationProvider provider) {
    GeolocationService service = geoLocationProviderContext.getService(provider);
    Result result = geolocationService.getLatLongByCity(city);
    return ResponseEntity.ok().body(result);
  }
}

Hiç yorum yok:

Yorum Gönder