[SPRING] 스프링 빈을 동적으로 주입
SPRING스프링 빈을 동적으로 주입
자바 스프링 웹 애플리케이션에서는 동적으로 콩을 주입 할 수 있기를 원합니다. 예를 들어, 나는 2 개의 다른 구현을 가진 인터페이스를 가지고있다 :
내 응용 프로그램에서 주입을 구성하는 몇 가지 속성 파일을 사용하고 있습니다 :
#Determines the interface type the app uses. Possible values: implA, implB
myinterface.type=implA
내 주사는 실제로 속성 파일의 속성 값을 조건부로 릴레이하여로드합니다. 예를 들어이 경우 myinterface.type = implA 어디서나 MyInterface를 삽입하면 삽입 될 구현은 ImplA가 될 것입니다. (조건 적 주석을 확장하여 구현했습니다).
런타임 중에 속성을 변경하면 서버를 다시 시작하지 않고 다음과 같이됩니다.
내 상황을 상쾌하게 생각했지만 문제가 생깁니다. 아마도 주입기에 setter를 사용하고 속성을 다시 구성하면 해당 setter를 다시 사용하는 것이 좋습니다. 그러한 요구 사항에 대한 실천이 있습니까?
어떤 아이디어?
최신 정보
필자는 두 가지 구현 (ImplA 및 ImplB)을 보유하고 관련 속성을 쿼리하여 올바른 것을 반환하는 factory / registry를 사용할 수 있다고 제안했습니다. 제가 그렇게한다면 저는 여전히 두 번째 도전, 즉 환경을 가지고 있습니다. 예를 들어 내 레지스트리가 다음과 같이 보이는 경우
@Service
public class MyRegistry {
private String configurationValue;
private final MyInterface implA;
private final MyInterface implB;
@Inject
public MyRegistry(Environmant env, MyInterface implA, MyInterface ImplB) {
this.implA = implA;
this.implB = implB;
this.configurationValue = env.getProperty("myinterface.type");
}
public MyInterface getMyInterface() {
switch(configurationValue) {
case "implA":
return implA;
case "implB":
return implB;
}
}
}
속성이 변경되면 내 환경을 다시 주입해야합니다. 그 어떤 제안?
나는 env를 생성자 대신에 메서드 내에서 질의 할 수 있다는 것을 알고 있지만 이것은 성능 저하이며 또한 환경을 다시 주입하기 위해 (다시 말해 아마도 setter injection을 사용하는 것) 생각하고 싶습니다.
해결법
-
==============================
1.나는이 작업을 가능한 한 간단하게 유지할 것이다. 조건부로 시작시 MyInterface 인터페이스의 조건부 구현을로드 한 다음 동일한 인터페이스의 다른 구현을 동적으로로드하는 이벤트를 실행하는 대신이 문제를 다른 방식으로 처리하고 구현하고 유지 관리하는 것이 훨씬 간단합니다.
나는이 작업을 가능한 한 간단하게 유지할 것이다. 조건부로 시작시 MyInterface 인터페이스의 조건부 구현을로드 한 다음 동일한 인터페이스의 다른 구현을 동적으로로드하는 이벤트를 실행하는 대신이 문제를 다른 방식으로 처리하고 구현하고 유지 관리하는 것이 훨씬 간단합니다.
우선, 가능한 모든 구현을로드합니다.
@Component public class MyInterfaceImplementationsHolder { @Autowired private Map<String, MyInterface> implementations; public MyInterface get(String impl) { return this.implementations.get(impl); } }
이 빈은 MyInterface 인터페이스의 모든 구현을위한 홀더입니다. 아무것도 여기에 마법, 그냥 일반적인 봄 autowiring 동작.
이제 MyInterface의 특정 구현을 삽입해야하는 곳이면 인터페이스를 사용하여 구현할 수 있습니다.
public interface MyInterfaceReloader { void changeImplementation(MyInterface impl); }
그런 다음 구현 변경을 통보 받아야하는 모든 클래스에 대해 MyInterfaceReloader 인터페이스를 구현하게하십시오. 예를 들면 :
@Component public class SomeBean implements MyInterfaceReloader { // Do not autowire private MyInterface myInterface; @Override public void changeImplementation(MyInterface impl) { this.myInterface = impl; } }
마지막으로, MyInterface를 속성으로 가지는 모든 bean의 구현을 실제로 변경하는 bean이 필요하다.
@Component public class MyInterfaceImplementationUpdater { @Autowired private Map<String, MyInterfaceReloader> reloaders; @Autowired private MyInterfaceImplementationsHolder holder; public void updateImplementations(String implBeanName) { this.reloaders.forEach((k, v) -> v.changeImplementation(this.holder.get(implBeanName))); } }
이것은 MyInterfaceReloader 인터페이스를 구현하는 모든 bean을 간단히 autowire하고 각각을 holder에서 가져 와서 인수로 전달 된 새로운 구현으로 갱신합니다. 다시 말하지만 일반적인 스프링 autowiring 규칙.
구현을 변경하고자 할 때마다 새로운 구현의 bean 이름을 사용하여 updateImplementations 메소드를 호출해야합니다. 즉, 클래스의 하위 낙타 케이스 단순 이름, 즉 MyImplA 및 MyImplB 클래스의 myImplA 또는 myImplB입니다.
시작할 때이 메소드를 호출하여 MyInterfaceReloader 인터페이스를 구현하는 모든 bean에 초기 구현을 설정해야합니다.
-
==============================
2.나는 org.apache.commons.configuration.PropertiesConfiguration과 org.springframework.beans.factory.config.ServiceLocatorFactoryBean을 사용하여 유사한 문제를 해결했다 :
나는 org.apache.commons.configuration.PropertiesConfiguration과 org.springframework.beans.factory.config.ServiceLocatorFactoryBean을 사용하여 유사한 문제를 해결했다 :
VehicleRepairService가 인터페이스가되도록합시다.
public interface VehicleRepairService { void repair(); }
CarRepairService 및 TruckRepairService는이를 구현하는 두 가지 클래스가 있습니다.
public class CarRepairService implements VehicleRepairService { @Override public void repair() { System.out.println("repair a car"); } } public class TruckRepairService implements VehicleRepairService { @Override public void repair() { System.out.println("repair a truck"); } }
나는 서비스 팩토리를위한 인터페이스를 만든다 :
public interface VehicleRepairServiceFactory { VehicleRepairService getRepairService(String serviceType); }
구성을 구성 클래스로 사용하십시오.
@Configuration() @ComponentScan(basePackages = "config.test") public class Config { @Bean public PropertiesConfiguration configuration(){ try { PropertiesConfiguration configuration = new PropertiesConfiguration("example.properties"); configuration .setReloadingStrategy(new FileChangedReloadingStrategy()); return configuration; } catch (ConfigurationException e) { throw new IllegalStateException(e); } } @Bean public ServiceLocatorFactoryBean serviceLocatorFactoryBean() { ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean(); serviceLocatorFactoryBean .setServiceLocatorInterface(VehicleRepairServiceFactory.class); return serviceLocatorFactoryBean; } @Bean public CarRepairService carRepairService() { return new CarRepairService(); } @Bean public TruckRepairService truckRepairService() { return new TruckRepairService(); } @Bean public SomeService someService(){ return new SomeService(); } }
FileChangedReloadingStrategy를 사용하면 등록 정보 파일을 변경할 때 구성이 다시로드됩니다.
service=truckRepairService #service=carRepairService
서비스에 구성 및 팩토리가 있으면, 해당 특성의 현재 값을 사용하여 공장에서 적절한 서비스를 얻을 수 있습니다.
@Service public class SomeService { @Autowired private VehicleRepairServiceFactory factory; @Autowired private PropertiesConfiguration configuration; public void doSomething() { String service = configuration.getString("service"); VehicleRepairService vehicleRepairService = factory.getRepairService(service); vehicleRepairService.repair(); } }
희망이 도움이됩니다.
-
==============================
3.내가 올바르게 이해한다면 주입 된 객체 인스턴스를 대체하는 것이 아니라 인터페이스 메소드 호출 중에 다른 구현을 사용하는 것이 런타임에 어떤 조건에 달려 있다는 것입니다.
내가 올바르게 이해한다면 주입 된 객체 인스턴스를 대체하는 것이 아니라 인터페이스 메소드 호출 중에 다른 구현을 사용하는 것이 런타임에 어떤 조건에 달려 있다는 것입니다.
그렇다면 ProxyFactoryBean과 함께 Sring TargetSource 메커니즘을 살펴볼 수 있습니다. 요점은 프록시 객체가 여러분의 인터페이스를 사용하는 빈에 주입되고 모든 인터페이스 메소드 호출이 TargetSource 타겟에 보내질 것이라는 점이다.
아래의 예를 살펴보십시오.
ConditionalTargetSource.java
@Component public class ConditionalTargetSource implements TargetSource { @Autowired private MyRegistry registry; @Override public Class<?> getTargetClass() { return MyInterface.class; } @Override public boolean isStatic() { return false; } @Override public Object getTarget() throws Exception { return registry.getMyInterface(); } @Override public void releaseTarget(Object target) throws Exception { //Do some staff here if you want to release something related to interface instances that was created with MyRegistry. } }
applicationContext.xml
<bean id="myInterfaceFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces" value="MyInterface"/> <property name="targetSource" ref="conditionalTargetSource"/> </bean> <bean name="conditionalTargetSource" class="ConditionalTargetSource"/>
SomeService.java
@Service public class SomeService { @Autowired private MyInterface myInterfaceBean; public void foo(){ //Here we have `myInterfaceBean` proxy that will do `conditionalTargetSource.getTarget().bar()` myInterfaceBean.bar(); } }
또한 MyInterface 구현을 모두 Spring Bean으로 설정하고 Spring 컨텍스트에 두 인스턴스를 동시에 포함 할 수없는 경우 대상 구현 클래스에서 프로토 타입 대상 bean 범위 및 조건부 주석으로 ServiceLocatorFactoryBean을 사용하려고 할 수 있습니다. 이 접근법은 MyRegistry 대신 사용할 수 있습니다.
추신 응용 프로그램 컨텍스트 새로 고침 작업도 원하는 작업을 수행 할 수 있지만 성능 오버 헤드와 같은 다른 문제가 발생할 수 있습니다.
-
==============================
4.이것은 중복 된 질문 일 수도 있고 적어도 유사 할 수도 있습니다. 어쨌든 저는 이런 종류의 질문에 대답했습니다. 스프링 빈 부분 autowire 프로토 타입 생성자
이것은 중복 된 질문 일 수도 있고 적어도 유사 할 수도 있습니다. 어쨌든 저는 이런 종류의 질문에 대답했습니다. 스프링 빈 부분 autowire 프로토 타입 생성자
런타임시 의존성을 위해 다른 빈을 원할 때 프로토 타입 범위를 사용해야한다. 그런 다음 구성을 사용하여 프로토 타입 bean의 다른 구현을 리턴 할 수 있습니다. 자신을 되 돌리려면 구현 로직을 처리해야합니다 (두 개의 다른 싱글 톤 빈을 반환 할 수도 있습니다).하지만 새 빈을 원한다고 말하면 구현을 반환하는 로직은 SomeBeanWithLogic이라는 빈에 있습니다. .isSomeBooleanExpression (), 다음 구성을 만들 수 있습니다 :
@Configuration public class SpringConfiguration { @Bean @Autowired @Scope("prototype") public MyInterface createBean(SomeBeanWithLogic someBeanWithLogic ) { if (someBeanWithLogic .isSomeBooleanExpression()) { return new ImplA(); // I could be a singleton bean } else { return new ImplB(); // I could also be a singleton bean } } }
컨텍스트를 다시로드 할 필요가 없습니다. 예를 들어, 런타임시 bean의 구현을 변경하려면 위의 것을 사용하십시오. 이 bean이 singleton bean이나 뭔가 이상한 것의 생성자에서 사용 되었기 때문에 정말로 애플리케이션을 다시로드 할 필요가 있다면, 당신은 당신의 디자인을 다시 생각할 필요가있다. 컨텍스트를 다시로드하여 다른 런타임 동작을 달성하기 위해 싱글 톤 빈을 다시 작성하면 안됩니다. 즉, 필요하지 않습니다.
편집이 답변의 첫 번째 부분은 콩을 동적으로 주입하는 것에 대한 질문에 대답했습니다. 묻는대로,하지만 질문은 더 많은 것 같아요 : '런타임에 싱글 톤 빈의 구현을 어떻게 바꿀 수 있습니까?' 프록시 디자인 패턴을 사용하여이를 수행 할 수 있습니다.
interface MyInterface { public String doStuff(); } @Component public class Bean implements MyInterface { boolean todo = false; // change me as needed // autowire implementations or create instances within this class as needed @Qualifier("implA") @Autowired MyInterface implA; @Qualifier("implB") @Autowired MyInterface implB; public String doStuff() { if (todo) { return implA.doStuff(); } else { return implB.doStuff(); } } }
-
==============================
5.FileChangedReloadingStrategy는 프로젝트를 배포 조건에 크게 의존하게 만듭니다. WAR / EAR을 컨테이너로 분해해야하며 모든 상황에서 항상 충족되지 않는 조건이 파일 시스템에 직접 액세스해야합니다. 및 환경.
FileChangedReloadingStrategy는 프로젝트를 배포 조건에 크게 의존하게 만듭니다. WAR / EAR을 컨테이너로 분해해야하며 모든 상황에서 항상 충족되지 않는 조건이 파일 시스템에 직접 액세스해야합니다. 및 환경.
-
==============================
6.속성 값에 Spring @Conditional을 사용할 수 있습니다. 두 콩에 동일한 이름을 지정하면 단 하나의 인스턴스 만 생성되므로 작동됩니다.
속성 값에 Spring @Conditional을 사용할 수 있습니다. 두 콩에 동일한 이름을 지정하면 단 하나의 인스턴스 만 생성되므로 작동됩니다.
서비스 및 구성 요소에 @ 조건을 사용하는 방법에 대해 살펴보십시오. http://blog.codeleak.pl/2015/11/how-to-register-components-using.html
from https://stackoverflow.com/questions/40105044/inject-spring-bean-dynamically by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] URL에서 jsessionid 제거 (0) | 2019.03.04 |
---|---|
[SPRING] 스프링 보안으로 세션 ID 검색하기 (0) | 2019.03.04 |
[SPRING] Spring 애플리케이션 컨텍스트에 사전 구성된 Bean 추가하기 (0) | 2019.03.04 |
[SPRING] 봄 - 트랜잭션 읽기 전용 (0) | 2019.03.04 |
[SPRING] MongoRepository에서 일부 HTTP 메소드를 내보내는 것을 방지하려면 어떻게해야합니까? (0) | 2019.03.04 |