1.4.6 메소드 주입 by sh

대부분의 응용 프로그램 시나리오에서 컨테이너의 대부분의 bean은 singleton 입니다. 싱글 톤 빈이 다른 싱글 톤 빈과 협업해야하거나 싱글 톤이 아닌 빈이 다른 싱글 톤이 아닌 빈과 협업해야하는 경우 일반적으로 빈을 다른 빈의 프로퍼티로 정의하여 의존성을 처리합니다. 빈의 수명주기(라이프사이클)이 다른 경우 문제가 발생합니다. 싱글 톤 빈 A가 싱글톤이 아닌 A (프로토 타입) bean 을 사용할 필요가 있다고 가정하십시오. 아마 각 메서드 호출은은 A에서 발생합니다. 컨테이너는 싱글톤 빈 A를 한 번만 생성하므로 프로퍼티를 설정할 수있는 기회도 한 번만 있습니다. 컨테이너는 빈 B가 필요할 때마다 b빈 B의 새 인스턴스를 가진 빈A를 제공 할 수 없습니다.

해결책은 제어의 역전(inversion of control)을 막는 것입니다. ApplicationContextAware인터페이스 를 구현함으로써 빈 A가 빈B를 필요로 할 때마다 컨테이너가 (일반적으로 새로운) 빈 B 대한 getBean("B")호출을 하게 함으로써 빈 A가 컨테이너인식하게 할 수 있으며 다음 예제에서는이 접근 방식을 보여줍니다.

// 어떤 프로세싱을 수행하기 위해 상태가 있는 커맨드 스타일의 클래스를 사용하는 클래스
package fiona.apple;

// 스프링 API 임포트
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 command = createCommand();
         // (원컨대 완전히 새로운) Command 인스턴스에 상태를 설정한다
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // 스프링 API 의존성에 주의해라!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

앞의 예제는 비즈니스 코드가 스프링 프레임 워크를 인식하고 결합하기 때문에 바람직하지 않습니다. Spring IoC 컨테이너의 다소 고급 기능인 메소드 삽입 (Method Injection)을 통해이 유스 케이스를 깔끔하게 처리 할 수있다.

이 블로그 엔트리 에서 Method Injection에 대한 동기를 더 읽을 수 있습니다 .

Lookup Method Injection

메서드 주입 검색은 컨테이너에서 또 다른 이름있는 빈에 대해 검색 결과를 리턴하도록 컨테이너가 컨테이너가 관리하는 빈의메서드를 오버라이드한다. 이전 섹션에서 설명한 시나리오처럼 검색은 보통 프로토타입 빈과 관계된다. 스프링 프레임워크는 메서드를 오버라이드 하는 서브클래스를 동적으로 생성하기 위해 CGLIB 라이브러리에서 바이트코드 생성을 사용해서 이 메서드 주입을 구현한다.

  • 이 동적으로 서브클래스를 만드는 작업을 하려면 클래스패스에 CGLIB jar파일을 두어야 한다. 스프링 프레임워크가 서브클래스를 생성하는 클래스는 final이 될 수 없고 오버라이드 되는 메서드도 final이 될 수 없다. 또한 abstract 메서드가 있는 클래스를 테스트하려면 개발자가 직접 클래스의 서브클래스를 만들고 abstract 메서드의 스텁(stub) 구현을 제공해야 한다. 마지막으로 메서드 주입의 타겟이 되는 객체들은 직렬화 될 수 없다.

이전 코드 예제에서CommandManager 클래스를 보면 스프링 컨테이너가createCommand() 메서드의 구현을 동적으로 오버라이드한다는 것을 알 수 있다. 수정한 예제에서 볼 수 있듯이CommandManager클래스는 스프링에 대한 어떤 의존성도 갖지 않을 것이다.

package fiona.apple;

// 더는 스프링을 임포트하지 않는다! 

public abstract class CommandManager {

    public Object process(Object commandState) {
        // 적절한 Command의 새로운 인스턴스르 붙잡는다
        Command command = createCommand();
         // (원컨대 완전히 새로운) Command 인스턴스에 상태를 설정한다
        command.setState(commandState);
        return command.execute();
    }

    // 괜찮지만... 이 메서드의 구현은 어디에 있는가?
    protected abstract Command createCommand();
}

주입되는 메서드가 있는는 클라이언트 클래스에서(이 경우에는CommandManager) 주입되는 메서드는 다음과 같은 형식의 시그니처여야 한다.

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

메서드가 abstract이라면 동적으로 생성된 서브클래스가 메서드를 구현한다. abstract가 아니라면 동적으로 생성된 서브클래스는 원래의 클래스에서 정의된 구현 메서드를 오버라이드한다. 예를 들면 다음과 같다.

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

이 빈은 command빈의 새로운 인스턴스가 필요할 때마다 자신의 createCommand() 메서드를 호출하는 commandManager이다. command 빈이 실제로 필요하다면 프로토타입으로 배포해야 한다. command 빈이 싱글톤으로 배포된다면 command 빈은 항상 같은 인스턴스가 리턴될 것이다.

또는 주석 기반 구성 요소 모델 내 @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();
}

또는 관용적으로 보면, 조회 메소드의 선언 된 리턴 유형에 대해 해결되는 대상 bean에 의존 할 수 있습니다.

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

일반적으로 추상 주석 클래스가 기본적으로 무시되는 Spring의 컴포넌트 검색 규칙과 호환되도록 이러한 주석 첨부 조회 메소드를 구체적인 스텁 구현으로 선언해야합니다. 이 제한은 명시 적으로 등록되거나 명시 적으로 가져온 Bean 클래스에는 적용되지 않습니다.

다르게 범위가 지정된 대상 빈을 액세스하는 또 다른 방법은 ObjectFactory/Provider주입 지점입니다. Scoped Bean을 종속성으로 참조하십시오 .

또한 ServiceLocatorFactoryBean(org.springframework.beans.factory.config패키지 안에) 유용 할 수 있습니다.

임의의 메서드 교체

검색 메서드 주입보다 덜 유용한 메서드 주입의 형태는 관리하는 빈에서 임의의 메서드를 다른 메서드 구현으로 교체하는 것이다. 사용자들은 이 기능이 필요할 때까지 이 섹션의 남은 부분은 읽지 않아도 좋다. XML 기반의 설정 메타데이터에서 배포된 빈에 대해 이미 존재하는 메서드 구현을 다른 메서드로 교체하기 위해 replaced-method 요소를 사용할 수 있다. 다음 클래스와 오버라이드 할 computeValue메서드를 보자. 룩업 메소드 삽입보다 덜 유용한 메소드 주입은 관리 빈의 임의의 메소드를 다른 메소드 구현으로 대체하는 기능입니다. 실제로이 기능이 필요할 때까지이 섹션의 나머지 부분은 건너 뛰어도됩니다.

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

Last updated