2024. 11. 14. 12:28ㆍSpring Framework/Spring IoC
🌱 Spring Method Injection 정리
Spring에서는 빈의 생명주기가 다를 때 발생하는 문제를 해결하기 위해 "메서드 주입(Method Injection)" 기능을 제공합니다.
일반적인 의존성 주입(Dependency Injection) 방식으로 해결할 수 없는 경우 Lookup Method Injection, Arbitrary Method Replacement 등의 기법을 활용할 수 있습니다.
1️⃣ Method Injection이란?
📌 기본 개념
🔹 싱글톤 빈이 프로토타입 빈을 참조할 때, 새로운 인스턴스를 주입받아야 하는 경우 발생하는 문제를 해결하는 방법
🔹 Spring 컨테이너가 런타임에 동적으로 메서드를 오버라이드하여, 새로운 프로토타입 빈을 반환하도록 구현
🔹 Spring이 제공하는 고급 기능 중 하나로, 특정한 경우에만 사용됨
2️⃣ Method Injection이 필요한 상황
Spring의 대부분의 빈은 싱글톤(Singleton)으로 관리됩니다.
하지만 때때로 싱글톤 빈이 프로토타입(Prototype) 빈을 사용해야 하는 상황이 발생할 수 있습니다.
🔥 문제 상황
- 싱글톤 빈 A가 프로토타입 빈 B를 사용해야 한다고 가정
- 싱글톤 빈 A는 한 번만 생성되므로, 프로토타입 빈 B도 한 번만 주입됨
- 새로운 요청마다 새로운 B 인스턴스를 생성할 수 없음 → 문제 발생
@Component
public class SingletonBean {
private final PrototypeBean prototypeBean;
@Autowired
public SingletonBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public void usePrototype() {
System.out.println("Using prototype: " + prototypeBean);
}
}
✅ 싱글톤 빈은 한 번만 생성되므로 PrototypeBean
도 한 번만 주입됨 → 새로운 인스턴스를 받을 수 없음
3️⃣ 해결 방법
✅ 방법 1: ApplicationContext
를 활용한 Bean 조회 (비추천)
Spring 컨테이너를 직접 사용하여 필요할 때마다 새로운 빈을 가져오는 방식
@Component
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Object process(Map<String, Object> commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
return this.applicationContext.getBean("command", Command.class);
}
}
📌 단점
❌ Spring 프레임워크에 종속적 → 비즈니스 로직이 Spring API에 의존하게 됨
❌ 테스트 및 유지보수 어려움
🚀 Spring이 제공하는 더 나은 해결책: "Lookup Method Injection"
✅ 방법 2: Lookup Method Injection (@Lookup
사용)
- Spring 컨테이너가 특정 메서드를 오버라이드하여, 새로운 프로토타입 빈을 반환하도록 만듦
- 비즈니스 로직이 Spring API에 의존하지 않음 → 더 깔끔한 코드
📌 예제: @Lookup
활용
@Component
public abstract class CommandManager {
public Object process(Map<String, Object> commandState) {
Command command = createCommand(); // 새 Command 인스턴스 생성
command.setState(commandState);
return command.execute();
}
@Lookup("command") // "command" 빈을 주입받음 (새로운 인스턴스 반환)
protected abstract Command createCommand();
}
📌 @Configuration
설정
@Configuration
public class AppConfig {
@Bean
@Scope("prototype") // Command 빈을 프로토타입으로 설정
public Command command() {
return new ConcreteCommand();
}
@Bean
public CommandManager commandManager() {
return new CommandManager() {}; // 익명 서브클래스 생성
}
}
📌 @Lookup
을 활용한 방식의 특징
✅ Spring API에 의존하지 않음 → ApplicationContextAware
보다 훨씬 깔끔
✅ 필요할 때마다 새로운 프로토타입 빈을 반환
✅ Spring이 자동으로 메서드를 오버라이드
✅ 방법 3: ObjectFactory
또는 Provider
사용
- Spring이 제공하는
ObjectFactory
또는Provider
를 활용하여 프로토타입 빈을 생성 - Spring 컨테이너를 직접 참조하지 않음 →
ApplicationContextAware
방식보다 개선된 방법
📌 ObjectFactory
활용 예제
@Component
public class CommandManager {
private final ObjectFactory<Command> commandFactory;
@Autowired
public CommandManager(ObjectFactory<Command> commandFactory) {
this.commandFactory = commandFactory;
}
public Object process(Map<String, Object> commandState) {
Command command = commandFactory.getObject(); // 새로운 인스턴스 반환
command.setState(commandState);
return command.execute();
}
}
📌 Provider
활용 예제
@Component
public class CommandManager {
private final Provider<Command> commandProvider;
@Autowired
public CommandManager(Provider<Command> commandProvider) {
this.commandProvider = commandProvider;
}
public Object process(Map<String, Object> commandState) {
Command command = commandProvider.get(); // 새로운 인스턴스 반환
command.setState(commandState);
return command.execute();
}
}
📌 특징
✅ Spring 컨테이너를 직접 참조하지 않음 → 코드가 더 깔끔하고 유연함
✅ 필요할 때마다 새로운 프로토타입 빈을 반환
4️⃣ Arbitrary Method Replacement (임의 메서드 대체)
- 기존 빈의 특정 메서드를 다른 메서드로 대체하는 기능
- 일반적인 DI보다 활용 빈도가 낮지만, 특수한 경우 사용 가능
✅ 예제: 기존 메서드 동적 대체
public class MyValueCalculator {
public String computeValue(String input) {
return "Original: " + input;
}
}
📌 MethodReplacer
인터페이스를 사용하여 동적 메서드 대체
public class ReplacementComputeValue implements MethodReplacer {
@Override
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
return "Replaced: " + args[0];
}
}
📌 XML 설정으로 기존 메서드 교체
<bean id="myValueCalculator" class="MyValueCalculator">
<replaced-method name="computeValue" replacer="replacementComputeValue"/>
</bean>
<bean id="replacementComputeValue" class="ReplacementComputeValue"/>
📌 특징
✅ 런타임에 특정 메서드를 동적으로 변경 가능
✅ 프록시 패턴을 사용할 필요 없이 기존 메서드 로직을 교체 가능
🎯 Summary
✅ Method Injection 활용 방법
방법 | 특징 |
---|---|
ApplicationContextAware |
Spring 컨테이너에서 직접 getBean() 호출 (비추천) |
@Lookup |
컨테이너가 자동으로 메서드를 오버라이드하여 새로운 프로토타입 빈을 반환 (추천) |
ObjectFactory / Provider |
필요할 때마다 새로운 빈을 반환하도록 설정 (추천) |
Arbitrary Method Replacement | 기존 메서드를 런타임에 동적으로 교체 |
📌 Method Injection을 적절히 활용하면 유지보수성과 유연성이 높은 애플리케이션을 만들 수 있습니다! 🚀
Method Injection :: Spring Framework
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the depende
docs.spring.io
'Spring Framework > Spring IoC' 카테고리의 다른 글
Annotation-based Container Configuration (0) | 2024.11.14 |
---|---|
Bean Scopes (0) | 2024.11.14 |
Using depends-on, Lazy-initialized Beans, Autowiring Collaborators (0) | 2024.11.14 |
Dependencies (0) | 2024.11.14 |
Dependency Injection (0) | 2024.06.11 |