-
6. 그레이들 멀티프로젝트 - 데이터베이스 셋업 및 활용(2) - Functional Interface, dynamic bean selector스프링개발자/301 - 아키텍처 2020. 10. 7. 05:51
[배경]
세 가지 탈것의 종류가 있다; 자동차, 트럭, 자전거
각각의 고객은 이 세가지의 탈 것 중에 하나를 고를 수 있다.
자동차를 고른 고객은 car instance가 생성되어 프로그램에 사용되게 되고
트럭을 고른 고객은 truck instance가 생성되어 프로그램이 진행된다.
각각의 탈 것 instance에 따라서 속도, 무게, 부피 등등을 다르게 할 수 있다.
이렇게 하면, code base change없이 데이터베이스 값 변경만으로 dynamic한 request call이 가능하다.
1. 모델 클래스 준비
2. 탈것들의 Bean Config 만들기
3. 프로그램 시작시에 설정 불러오기
4. 테스트
1. 모델 클래스 준비
자동차, 트럭, 자전거의 세 가지 탈것 모두 공통점이 있으니 탈 것이라는 인터페이스로 묶기
public static create method로 interface를 통해서 instance 관리하자.
package com.example.monorepo.vehicle; public interface Vehicle { boolean canLoadPassenger(int passengerConsumption); boolean canLoadGoods(int goodsConsumption); boolean canDrive(); }
package com.example.monorepo.vehicle; import lombok.Data; @Data public class Car implements Vehicle { private final int passengerCapacityLimit = 4; // person private final int goodsCapacityLimit = 100; // kg int passengerCapacityCurrent = 0; int goodsCapacityCurrent = 0; @Override public boolean canLoadPassenger(int passengerConsumption) { if (passengerCapacityCurrent + passengerConsumption > passengerCapacityLimit) return false; passengerCapacityCurrent += passengerConsumption; return true; } @Override public boolean canLoadGoods(int goodsConsumption) { if (goodsCapacityCurrent + goodsConsumption > goodsCapacityLimit) return false; goodsCapacityCurrent += goodsConsumption; return true; } @Override public boolean canDrive() { return (passengerCapacityCurrent * goodsCapacityCurrent > 250) ? false : true; } public static Car create(){ return new Car(); } }
package com.example.monorepo.vehicle; import lombok.Data; @Data public class Truck implements Vehicle { private final int passengerCapacityLimit = 2; // person private final int goodsCapacityLimit = 500; // kg int passengerCapacityCurrent = 0; int goodsCapacityCurrent = 0; @Override public boolean canLoadPassenger(int passengerConsumption) { if (passengerCapacityCurrent + passengerConsumption > passengerCapacityLimit) return false; passengerCapacityCurrent += passengerConsumption; return true; } @Override public boolean canLoadGoods(int goodsConsumption) { if (goodsCapacityCurrent + goodsConsumption > goodsCapacityLimit) return false; goodsCapacityCurrent += goodsConsumption; return true; } @Override public boolean canDrive() { return (passengerCapacityCurrent * goodsCapacityCurrent > 950) ? false : true; } public static Truck create(){ return new Truck(); } }
package com.example.monorepo.vehicle; import lombok.Data; @Data public class Bicycle implements Vehicle { private final int passengerCapacityLimit = 1; // person private final int goodsCapacityLimit = 20; // kg int passengerCapacityCurrent = 0; int goodsCapacityCurrent = 0; public boolean canLoadPassenger(int passengerConsumption) { if (passengerCapacityCurrent + passengerConsumption > passengerCapacityLimit) return false; passengerCapacityCurrent += passengerConsumption; return true; } public boolean canLoadGoods(int goodsConsumption) { if (goodsCapacityCurrent + goodsConsumption > goodsCapacityLimit) return false; goodsCapacityCurrent += goodsConsumption; return true; } public boolean canDrive() { return (passengerCapacityCurrent * goodsCapacityCurrent > 20) ? false : true; } public static Bicycle create(){ return new Bicycle(); } }
2. 탈 것들 Bean Config 만들기
Functional Interface를 통해서 Vehicle클래스를 리턴한다.
Bean Config을 만들고, Primary설정
@FunctionalInterface public interface VehicleManagerConfiguration { Vehicle getVehicle(); }
@Configuration public class VehicleManagerBeanConfig { @Primary @Bean("car") VehicleManagerConfiguration carManager() { return Car::create; } @Bean("truck") VehicleManagerConfiguration truckManager() { return Truck::create; } @Bean("bicycle") VehicleManagerConfiguration bicycleManager() { return Bicycle::create; } }
Vehicle클래스가 아닌 VehicleManagerConfiguration을 리턴하는 이유는, Bean의 리턴타입이 functional interface이어야 하기 때문. Additional wrapper class이다.
3. 프로그램 시작시에 설정 불러오기
ApplicationContext를 이용하면, 어플리케이션이 동작될때에 추가 로직을 실행할 수 있다.
여기에서는 데이터베이스 값(Client의 vehicle설정 값)을 불러와서 그 이름의 bean를 찾아 리턴하자.
다시 한번, Vehicle instance 아닌 Vehicle Functional Interface를 리턴한다.
@Component public class VehicleSelector { private final ApplicationContext applicationContext; public VehicleSelector(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public VehicleManagerConfiguration determineVehicleForClient(Client client) { Optional<String> vehicleBeanName = Optional.ofNullable(client.getMonoParameters().get(MonoParameter.VEHICLE)); Map<String, VehicleManagerConfiguration> beansOfType = applicationContext.getBeansOfType(VehicleManagerConfiguration.class); return vehicleBeanName.map(x -> beansOfType.getOrDefault(x, applicationContext.getBean(VehicleManagerConfiguration.class))) .orElse(applicationContext.getBean(VehicleManagerConfiguration.class)); } }
4. 테스트
@Service public class VehicleService { @Autowired private VehicleSelector vehicleSelector; // ... public Vehicle getMyVehicle(Client client){ return vehicleSelector.determineVehicleForClient(client).getVehicle(); } }
@RestController @RequestMapping(value = "/main") public class MainController { // ... @Autowired ClientService clientService; @Autowired VehicleService vehicleService; // ... @GetMapping("/vehicle") public String getClientVehicle(@RequestParam("guid") String clientId){ Client client = clientService.getClientById(clientId); return vehicleService.getMyVehicle(client).getClass().toString(); } }
브라우저 열고 다음의 값을 실행(GET)
http://localhost:8080/main/vehicle?guid=cac6cea0-8620-4d0f-be62-dd24168a40c2
class com.example.monorepo.vehicle.Car
http://localhost:8080/main/vehicle?guid=3f99ea1c-6c62-46ca-8e25-6f8ac34bb222
class com.example.monorepo.vehicle.Truck
http://localhost:8080/main/vehicle?guid=ff6fb705-b37a-415f-bcd5-7088936938e5
class com.example.monorepo.vehicle.Bicycle
완성된 코드
'스프링개발자 > 301 - 아키텍처' 카테고리의 다른 글
5. 그레이들 멀티프로젝트 - 데이터베이스 셋업 및 활용(1) - Entity, CollectionTable, 데이터베이스값 불러오기 (0) 2020.10.05 4. 그레이들 멀티프로젝트 - 유레카서버와 페인클라이언트 Eureka FeignClient (0) 2020.08.31 3. 그레이들 멀티프로젝트 - RestTemplate (0) 2020.08.30 2. 그레이들 멀티프로젝트 - 공통자원 활용(compile project) (0) 2020.08.30 1. 그레이들 멀티프로젝트 - 셋업하기 (0) 2020.08.30