[SPRING] Spring : 런타임시 인터페이스 구현을 변경하는 방법
SPRINGSpring : 런타임시 인터페이스 구현을 변경하는 방법
Java 개발자로서 필자는 종종 인터페이스의 여러 구현 중에서 선택해야합니다. 때로는이 선택을 한 번 수행 할 수 있지만 다른 경우에는 프로그램에서받는 다양한 입력에 따라 다른 구현이 필요합니다. 즉, 런타임에 구현을 변경할 수 있어야합니다. 이는 (사용자 입력을 기반으로 한) 일부 키를 적합한 인터페이스 구현에 대한 참조로 변환하는 도우미 객체를 통해 쉽게 달성 할 수 있습니다.
Spring을 사용하면 bean과 같은 객체를 디자인 할 수 있으며, 필요하다면 삽입 할 수 있습니다.
public class MyClass {
@Autowired
private MyHelper helper;
public void someMethod(String someKey) {
AnInterface i = helper.giveMeTheRightImplementation(someKey);
i.doYourjob();
}
}
자, 도우미를 어떻게 구현해야합니까? 이것을 시작해 보겠습니다.
@Service
public class MyHelper {
public AnInterface giveMeTheRightImplementation(String key) {
if (key.equals("foo")) return new Foo();
else if (key.equals("bar")) return new Bar();
else ...
}
}
이러한 솔루션에는 몇 가지 결함이 있습니다. 최악의 경우 중 하나는 도우미로부터 반환 된 인스턴스가 컨테이너에 알려지지 않아서 종속성 주입의 이점을 얻을 수 없다는 사실입니다. 즉, 내가 Foo 클래스를 다음과 같이 정의하더라도 :
@Service
public class Foo {
@Autowired
private VeryCoolService coolService;
...
}
... MyHelper에서 반환 한 Foo 인스턴스에는 coolService 필드가 제대로 초기화되지 않습니다.
이를 피하기 위해 자주 제안되는 해결 방법은 각 가능한 구현을 도우미에 삽입하는 것입니다.
@Service
public class MyHelper {
@Autowired
private Foo foo;
@Autowired
private Bar bar;
...
public AnInterface giveMeTheRightImplementation(String key) {
if (key.equals("foo")) return foo;
else if (key.equals("bar")) return bar;
else ...
}
}
그러나 나는 그런 해결책에 대한 큰 팬이 아니다. 나는 이렇게 우아하고 유지 보수가 잘되는 것을 찾는다.
@Service
public class MyHelper {
@Autowired
private ApplicationContext app;
public AnInterface giveMeTheRightImplementation(String key) {
return (AnInterface) app.getBean(key);
}
}
이것은 Spring의 ApplicationContext를 기반으로합니다.
비슷한 해결책은 ServiceLocatorFactoryBean 클래스를 사용하는 것입니다.
public interface MyHelper {
AnInterface giveMeTheRightImplementation(String key);
}
// Somewhere else, in Java config
@Bean
ServiceLocatorFactoryBean myHelper() {
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyHelper.class);
return bean;
}
그러나 저는 스프링 전문가가 아니기 때문에 더 나은 접근법이 있는지 궁금합니다.
해결법
-
==============================
1.원하는 것을 수행하는 표준 방법은 다음과 같아야합니다.
원하는 것을 수행하는 표준 방법은 다음과 같아야합니다.
interface YourInterface { void doSomething(); } public class YourClass { @Inject @Any Instance<YourInterface> anImplementation; public void yourMethod(String someInput) { Annotation qualifier = turnInputIntoQualifier(someInput); anImplementation.select(qualifier).get().doSomething(); } private Annotation turnInputIntoQualifier(String input) { ... } }
현재, 그러나 Spring은 그것을 지원하지 않는다. v5.x 용으로 계획되었습니다.) 그것은 작동해야합니다 응용 프로그램 서버.
Spring을 고집하고 싶다면 ServiceLocatorFactoryBean 기반 솔루션 아마 최고의 것입니다.
-
==============================
2.내 프로젝트에서이 접근법을 따른다. 이것은 절대 안전한 것은 아니지만 구성 코드가 매우 적어서 새로운 구현을 추가하는 측면에서 상당히 도움이됩니다.
내 프로젝트에서이 접근법을 따른다. 이것은 절대 안전한 것은 아니지만 구성 코드가 매우 적어서 새로운 구현을 추가하는 측면에서 상당히 도움이됩니다.
나는 이런 식으로 enum을 만든다.
enum Mapper{ KEY1("key1", "foo"), KEY2("key2", "bar") ; private String key; private String beanName; public static getBeanNameForKey(String key){ // traverse through enums using values() and return beanName for key } }
Foo와 Bar가 공통 인터페이스에서 구현한다고 가정 해 봅시다. 인터페이스라고 부르 자.
class ImplFactory{ @Autowired Map<String, AnInterface> implMap; // This will autowire all the implementations of AnInterface with the bean name as the key public AnInterface getImpl(string beanName){ implMap.get(beanName); } }
그리고 도우미 수업이 이렇게 보일거야.
@Service public class MyHelper { @Autowired ImplFactory factory; public AnInterface giveMeTheRightImplementation(String key) { String beanName = Mapper.getBeanNameForKey(key); factory.getImpl(beanName); } }
이 접근법의 장점 중 일부는, 1. 올바른 구현을 선택하는 데 시간이 오래 걸릴 수 있습니다. 2. 새 구현을 추가하려는 경우. 당신이해야만하는 일은 당신의 enum에 Mapper를 추가하는 것입니다 (당신의 새로운 Impl 클래스를 추가하는 것 외에는). 3. 원하는 impl 클래스에 대한 빈 이름을 구성 할 수도 있습니다 (스프링으로 기본 빈 이름을 지정하지 않으려는 경우). 이 이름은 팩토리 클래스의 맵 키입니다. 열거 형에서 사용해야하는 것.
편집하다: 빈에 사용자 정의 이름을 지정하려면 스테레오 타입 어노테이션 중 하나의 value 속성을 사용할 수 있습니다. 예를 들어. Impl을 @Component 또는 @Service로 주석 처리 한 경우 @Component ( "myBeanName1") 또는 @Service ( "myBeanName2")를 수행하십시오.
-
==============================
3.선언 할 때 bean의 이름을 지정할 수 있으며, 도우미는 주어진 유형의 bean을 리턴하도록 응용 프로그램 컨텍스트에 요청할 수 있습니다. Bean에 선언 된 범위를 기반으로 컨텍스트를 기반으로 단일 또는 다른 범위를 사용할 필요가 있거나 다시 사용할 수있는 경우 응용 프로그램 컨텍스트에서 새 인스턴스를 만들 수 있습니다. 이렇게하면 스프링 기능을 완전히 활용할 수 있습니다.
선언 할 때 bean의 이름을 지정할 수 있으며, 도우미는 주어진 유형의 bean을 리턴하도록 응용 프로그램 컨텍스트에 요청할 수 있습니다. Bean에 선언 된 범위를 기반으로 컨텍스트를 기반으로 단일 또는 다른 범위를 사용할 필요가 있거나 다시 사용할 수있는 경우 응용 프로그램 컨텍스트에서 새 인스턴스를 만들 수 있습니다. 이렇게하면 스프링 기능을 완전히 활용할 수 있습니다.
예를 들어
@Service public class MyHelper { @Autowired ApplicationContext applicationContext; public AnInterface giveMeTheRightImplementation(String key) { return context.getBean(key); } } @Service("foo") public class Foo implements AnInterface { }
from https://stackoverflow.com/questions/44000672/spring-how-to-change-interface-implementations-at-runtime by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] java.lang.ClassCastException : java.lang.Class를 java.lang.reflect.ParameterizedType에 캐스트 할 수 없다 (0) | 2019.04.23 |
---|---|
[SPRING] 스프링 부트 웹 애플리케이션이 JUnit에서 다시 시작해야합니다. (0) | 2019.04.23 |
[SPRING] RoboSpice는 OrmLite를 사용하여 JSON 배열을 유지합니다. (0) | 2019.04.23 |
[SPRING] 제네릭과 모치토 일치 (0) | 2019.04.23 |
[SPRING] Spring / Hibernate / JPA에서 PostgreSQL 함수 (저장 프로 시저)를 올바르게 호출하는 방법? (0) | 2019.04.23 |