[SPRING] Spring은 런타임시 bean 구현을 선택한다.
SPRINGSpring은 런타임시 bean 구현을 선택한다.
주석이있는 Spring Beans를 사용하고 있으며 런타임에 다른 구현을 선택해야합니다.
@Service
public class MyService {
public void test(){...}
}
예를 들어 Windows 플랫폼의 경우 MyServiceWin을 확장해야합니다. Linux 플랫폼의 경우 MyServiceLnx가 MyService를 확장해야합니다.
현재로서는 나는 단지 하나의 끔찍한 해결책을 안다.
@Service
public class MyService {
private MyService impl;
@PostInit
public void init(){
if(windows) impl=new MyServiceWin();
else impl=new MyServiceLnx();
}
public void test(){
impl.test();
}
}
XML 설정이 아닌 주석 만 사용하고 있다고 생각해주십시오.
해결법
-
==============================
1.빈 주입을 다음과 같이 구성으로 이동할 수 있습니다.
빈 주입을 다음과 같이 구성으로 이동할 수 있습니다.
@Configuration public class AppConfig { @Bean public MyService getMyService() { if(windows) return new MyServiceWin(); else return new MyServiceLnx(); } }
또는 프로파일 윈도우와 리눅스를 사용하고 서비스 구현에 @Profile ( "linux") 또는 @Profile ( "windows")과 같은 @Profile 주석을 달고 애플리케이션에이 프로필 중 하나를 제공 할 수 있습니다.
-
==============================
2.
public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getEnvironment().getProperty("os.name").contains("Linux"); } }
Windows와 동일합니다.
@Configuration public class MyConfiguration { @Bean @Conditional(LinuxCondition.class) public MyService getMyLinuxService() { return new LinuxService(); } @Bean @Conditional(WindowsCondition.class) public MyService getMyWindowsService() { return new WindowsService(); } }
@Service public class SomeOtherServiceUsingMyService { @Autowired private MyService impl; // ... }
-
==============================
3.아름다운 설정을 만들어 봅시다.
아름다운 설정을 만들어 봅시다.
우리가 Animal 인터페이스를 가지고 있고 개와 고양이 구현이 있다고 상상해보십시오. 우리는 다음과 같이 쓰고 싶다.
@Autowired Animal animal;
어떤 구현이 반환되어야합니까?
그래서 해결책은 무엇입니까? 문제를 푸는 방법은 여러 가지가 있습니다. @Qualifier와 Custom Conditions를 함께 사용하는 방법을 쓰겠습니다.
그래서 먼저 모든 사용자 정의 주석을 작성하겠습니다.
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) public @interface AnimalType { String value() default ""; }
및 구성 :
@Configuration @EnableAutoConfiguration @ComponentScan public class AnimalFactoryConfig { @Bean(name = "AnimalBean") @AnimalType("Dog") @Conditional(AnimalCondition.class) public Animal getDog() { return new Dog(); } @Bean(name = "AnimalBean") @AnimalType("Cat") @Conditional(AnimalCondition.class) public Animal getCat() { return new Cat(); } }
Bean의 이름은 AnimalBean입니다. 왜이 콩이 필요한거야? 왜냐하면 우리가 Animal 인터페이스를 삽입 할 때 우리는 단지 @Qualifier ( "AnimalBean")
또한 커스텀 조건에 값을 전달하기 위해 커스텀 주석을 작성했습니다.
이제 우리의 조건은 다음과 같습니다 ( "Dog"이름은 설정 파일 또는 JVM 매개 변수 또는 ...)
public class AnimalCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){ return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName()) .entrySet().stream().anyMatch(f -> f.getValue().equals("Dog")); } return false; } }
그리고 마지막으로 주사 :
@Qualifier("AnimalBean") @Autowired Animal animal;
-
==============================
4.모든 구현을 @Qualifier 주석이있는 팩토리에 자동으로 가져온 다음 팩토리에서 필요한 서비스 클래스를 반환하십시오.
모든 구현을 @Qualifier 주석이있는 팩토리에 자동으로 가져온 다음 팩토리에서 필요한 서비스 클래스를 반환하십시오.
public class MyService { private void doStuff(); }
내 Windows 서비스 :
@Service("myWindowsService") public class MyWindowsService implements MyService { @Override private void doStuff() { //Windows specific stuff happens here. } }
My Mac 서비스 :
@Service("myMacService") public class MyMacService implements MyService { @Override private void doStuff() { //Mac specific stuff happens here } }
내 공장 :
@Component public class MyFactory { @Autowired @Qualifier("myWindowsService") private MyService windowsService; @Autowired @Qualifier("myMacService") private MyService macService; public MyService getService(String serviceNeeded){ //This logic is ugly if(serviceNeeded == "Windows"){ return windowsService; } else { return macService; } } }
정말 까다로워 지길 원하면 enum을 사용하여 구현 클래스 유형을 저장 한 다음 enum 값을 사용하여 반환 할 구현을 선택하십시오.
public enum ServiceStore { MAC("myMacService", MyMacService.class), WINDOWS("myWindowsService", MyWindowsService.class); private String serviceName; private Class<?> clazz; private static final Map<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, ServiceStore>(); static { //This little bit of black magic, basically sets up your //static map and allows you to get an enum value based on a classtype ServiceStore[] namesArray = ServiceStore.values(); for(ServiceStore name : namesArray){ mapOfClassTypes.put(name.getClassType, name); } } private ServiceStore(String serviceName, Class<?> clazz){ this.serviceName = serviceName; this.clazz = clazz; } public String getServiceBeanName() { return serviceName; } public static <T> ServiceStore getOrdinalFromValue(Class<?> clazz) { return mapOfClassTypes.get(clazz); } }
그런 다음 공장에서 응용 프로그램 컨텍스트로 이동하여 인스턴스를 자체 맵으로 가져올 수 있습니다. 새로운 서비스 클래스를 추가 할 때 열거 형에 다른 항목을 추가하기 만하면됩니다.
public class ServiceFactory implements ApplicationContextAware { private final Map<String, MyService> myServices = new Hashmap<String, MyService>(); public MyService getInstance(Class<?> clazz) { return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName()); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { myServices.putAll(applicationContext.getBeansofType(MyService.class)); } }
이제 원하는 클래스 유형을 팩토리에 전달하면 필요한 인스턴스를 다시 제공 할 수 있습니다. 특히 서비스를 일반화하려는 경우 매우 유용합니다.
from https://stackoverflow.com/questions/34350865/spring-choose-bean-implementation-at-runtime by cc-by-sa and MIT license