메서드 대체
- 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를 하나만 사용할 것을 권장