Spring - 메서드 대체

 

메서드 대체를 이용하는 방법

메서드 대체


  • bean의 소스를 변경하지 않고 임의로 모든 메서드의 구현체를 바꿀 수 있음
  • 내부적으로 bean 클래스의 서브 클래스를 동적으로 생성
  • CGLIB 라이브러리를 사용하여 원래 메서드 호출을 MethodReplcaer 인터페이스를 구현한 다른 bean에 대한 호출로 바꿀 수 있음
...
public class ReplacementTarget {
	public String formatMessage(String msg) {
		return "<h1>" + msg + "</h1>";
	}

	public String formatMessage(Object msg) {
		return "<h1>" + msg + "</h1>";
	}
}
...
Public class FormatMessageReplacer implements MethodReplacer {

	/*
		reimplement : 반드시 구현해야 하는 메서드
		@params
		- Object arg0 : 원래 호출된 메서드를 가진 빈
		- Method method : 오버라이드할 메서드의 인스턴스
		- Object ...args : 메서드로 전달될 파라미터들
		@return
		- 원래 메서드에서 반환하는 값과 호환되는 값(동일 클래스 또는 서브 클래스)
	*/
	@Override
	public Object reimplement(Object arg0, Method method, Object... args) thorows Throwable {
		if (isFormatMessageMethod(method)) {
			String msg = (String) arg0;
			return "<h2>" + msg + "</h2>";
		} else {
			throw new IllegalArgumentException("Unable to reimplement method" + method.getName());
		}
	}

	// 리다이렉트 할 메서드인지 체크
	private boolean isFormatMessageMethod(Method method) {
		if (method.getParameterType().length != 1) {
			return false;
		}

		if (!("formatMessage".equals(method.getName()))) {
			return false;
		}

		if (method.getReturnType() != String.class) {
			return false;
		}

		if (method.getReturnTypes()[0] != String.class) {
			return false;
		}

		return true;
	}
}
<beans ...>
	<bean id="methodReplacer" class="com.apress...FormatMessageReplacer" />

	<bean id="replacementTarget" class="com.apress...ReplacementTarget">
		<!-- 대체할 메서드 지정 -->
		<replaced-method name="formatMessage" replacer="methodReplacer">
			<!-- 파라미터의 타이블 지정하여 메서드의 시그니처 확인 -->
			<arg-type>String</arg-type>
		</replaced-method>
	</bean>

	<bean id="standardTarget" class="com.apress...ReplacementTarget" />
</beans>
  • 메서드 대체는 성능 저하가 크며 대부분의 오버헤드는 CGLIB에서 생성한 하위 클래스 자체에서 발생한다.

메서드 대체를 사용해야 하는 경우

  • 단일 bean에 대한 특정 메서드만 대체하려는 경우 유용
  • 소스가 오픈되지 않은 외부 라이브러에서 기능을 변경하고자 할 때
  • 메서드나 오버로드된 메서드 그룹마다 MethodReplacer를 하나만 사용할 것을 권장