본문 바로가기

Method Injection

동일한 scope 의 bean 에 의존성을 가질 때, 일반적으로 bean 의 속성에 의존성을 추가하여 처리한다. 예를 들어 singleton bean 이 prototype bean 을 의존한다면, 컨테이너는 singleton bean 을 단 한번 생성하므로 필요할 때마다 prototype bean 을 제공할 수 없다.


아래는 ApplicationContextAware 를 구현하여 singleton bean 에서 prototype bean 의 새 인스턴스를 요청하는 예제이다.


package fiona.apple;
 
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
 
public class CommandManager implements ApplicationContextAware {
 
    private ApplicationContext applicationContext;
 
    public Object process(Map commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }
 
    protected Command createCommand() {
        // Spring API getBean() 를 의존하고 있다.
        return this.applicationContext.getBean("command", Command.class);
    }
 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
cs


위 예제에서 컨테이너가 createCommand() 메서드의 구현을 동적으로 재정의한다는 것을 알 수 있다. Spinrg Framework 와 비지니스 코드와 결합되어 있으므로 바람직하지 않지만, 컨테이너의 lookup 메소드 주입을 사용하면 이를 해결할 수 있다.


lookup 메소드 주입은 bean 의 메소드를 재정의하여, 다른 이름의 bean 으로 대체시키는 컨테이너의 기능이다. Spring Framework 는 메소드를 재정의하는 서브 클래스를 동적으로 생성하기 위해 CGLIB 라이브러리의 바이트 코드 생성을 사용한다. 이 때 서브 클래스화 할 클래스와 재정의 할 메소드는 final 이 될 수 없다. lookup 메소드 주입은 factory 메소드나 @Bean 에서는 작동하지 않는다.


주입될 메소드는 다음의 형식으로 작성한다.


<public|protected> [abstract] <return-type> theMethodName(no-arguments);


추상 메소드라면 동적으로 생성된 서브 클래스는 이 메소드를 구현하고, 그렇지 않으면 동적으로 생성된 서브 클래스가 원래 클래스에 정의된 메서드를 재정의한다. 아래 예제에서는 lookup 메소드 주입으로 Spring 과 결합되어 있지 않다.


package fiona.apple;
 
// no more Spring imports!
 
public abstract class CommandManager {
 
    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }
 
    protected abstract Command createCommand();
}
 
cs


<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- 의존성 주입 -->
</bean>
 
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>
cs


commandManager bean 은 myCommand bean 의 새 인스턴스가 필요할 때마다 createCommand() 를 호출한다.


또는 어노테이션 컴포넌트 모델에서 @Lookup 어노테이션을 통해 lookup 메소드를 선언할 수 있다.


public abstract class CommandManager {
 
    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }
 
    @Lookup("myCommand")
    protected abstract Command createCommand();
 
    // or
    // @Lookup
    // protected abstract MyCommand createCommand();
}
cs