Spring - 메서드 주입 1 : 룩업 메서드

 

룩업 메서드를 이용하여 의존성을 주입하는 방법

메서드 주입


  • 룩업 메서드 주입과 메서드 대체 2가지 방법이 있다.
  • 이 기능들을 위해 CGLIB의 동적 바이트코드 확장 기능을 사용한다.

룩업 메서드 주입

  • 서로 다른 라이프사이클을 가진 bean간의 의존성을 해결하기 위한 방법(싱글턴 - 비싱글턴)
  • 싱글턴 bean이 비싱글턴 bean에 의존하는 경우 비싱글턴 bean은 싱글턴이 되어버린다.
  • 싱글턴 bean에 ApplicationContextAware 인터페이스를 구현하여 처리한다.
  • 비싱글턴 bean의 인스턴스를 반환하는 룩업 메서드를 싱글턴 bean에 선언하여 동작한다.
  • 룩업 메서드는 구현 없이 정의만 하므로 구현 클래스를 abstract로 선언한다.
...
public class Singer {
	private String lyric = "I played a quick game of chess with the salt and pepper shaker";

	public void sing() {
		...
	}
}
...
public interface DemoBean  {
	Singer getMySinger();

	void doSomething();
}
  • 수정자 주입을 통해 의존성(Singer)을 가져오는 StandardLookupDemoBean 클래스
  • 이 경우 mySinger는 항상 동일한 인스턴스이다.
...
public class StandardLookupDemoBean implements DemoBean {

	private Singer mySinger;

	public void setMySinger(Singer mySinger) {
		this.mySinger = mySinger;
	}

	@Override
	public Singer getMySinger() {
		return this.mySinger;
	}

	@Override
	public void doSomething() {
		mySinger.sing();
	}
}
  • 메서드 주입을 통해 의존성(Singer)을 가져오는 AbstractLookupDemoBean 클래스
  • 이 경우 getMySinger()를 통해 반환되는 인스턴스는 항상 다른 인스턴스이다.
  • 매번 다른 인스턴스를 생성하기 때무에 성능이 매우 느리다.
  • 스프링은 CGLIB를 사용해 동적으로 AbstractLookupDemoBean 클래스의 하위 클래스를 생성한다.
...
public abstract class AbstractLookupDemoBean implements DemoBean {

	// lookup 메서드는 파라미터를 받아서는 안되며 의존할 bean을 반환해야 한다.
	public abstract Singer getMySinger();

	@Override
	public void doSomething() {
		getMySinger().sing();
	}
}
<beans ...>
	<bean id="singer"
		  class="com.apress...Singer"
		  scope="prototype" />

	<bean id="abstractLookupBean"
		  class="com.apress...AbstractLookupDemoBean">
		<!-- lookup 메서드에 대한 구성 추가 -->
		<lookup-method name="getMySinger" bean="singer" />
	</bean>

	<bean id="standardLookupBean"
		  class="com.apress...StandardLookupDemoBean">
		<property name="mySinger" reg="singer" />
	</bean>
</beans>

ApplicationContext를 이용한 추상 클래스의 인스턴스 생성은 룩업 메서드 주입을 이용할 경우만 혀용된다.


애노테이션을 이용한 록업 메서드 주입

...
@Component("singer")
@Scope("prototype")
public class Singer {
	private String lyric = "I played a quick game of chess with the salt and pepper shaker";

	public void sing() {
		...
	}
}
...
@Component("abstractLookupBean")
public abstract class AbstractLookupDemoBean implements DemoBean {

	@Lookup("singer")
	public abstract Singer getMySinger() {
		return null; // 메서드 내용은 동적으로 생성된 하위 클래스에서 오버라이드 됨
	}

	@Override
	public void doSomething() {
		getMySinger().sing();
	}
}
...
@Component("standardLookupBean")
public class StandardLookupDemoBean implements DemoBean {

	private Singer mySinger;

	@Autowired
	@Qualifier("singer")
	public void setMySinger(Singer mySinger) {
		this.mySinger = mySinger;
	}

	@Override
	public Singer getMySinger() {
		return this.mySinger;
	}

	@Override
	public void doSomething() {
		mySinger.sing();
	}
}
<beans ...>
	<!-- 애노테이션이 적용된 클래스가 포함된 패키지만 스캔하도록 한다. -->
	<context:component-scan base-package="come.apress..." />
</beans>

룩업 메서드 사용 시 주의할 점

  • IoC 목적으로만 사용되는 불필요한 정의를 비지니스 인터페이스에 선언하지 않아야 한다.
  • 룩업 메서드를 반드시 추상화 할 필요는 없으나 추상화 하는 것이 좋다.