동적 프록시
프록시를 동작을 원하는 개수만큼 생성해놓는게 아니라,
동적으로 객체를 만들 수 있다.
프록시를 적용할 코드를 하나 만들고, 동적 프록시 기술로 원하는 만큼 찍어내면 된다.
그러기 위해서는 기본적으로 자바가 어떤 클래스, 어떤 메서드던지 획득, 작동을 시킬 수 있어야 한다.
그래서 자바에서는 Reflection API를 제공한다.
Reflection API
대표적으로 package명으로 Class를 가져오고,
이름으로 해당 Class의 Method명을 가져오면 된다.
개인적으로는 해당 Class의 Method를 전부 가져와서
Enum으로 메서드명을 value로 가져와서 일치하면 실행시키는 식으로 개발했었다.
예시
@Slf4j
public class ReflectionTest {
/**
* Reflection이 필요한 상황
*/
@Test
void reflection0() {
Hello target = new Hello();
/**
* 호출하는 메서드만 다르고 나머지는 동일한 동작.
* 공통 로직 1과 2를 하나의 메서드로 뽑아서 합칠 수 있을까?
*/
//공통 로직1 시작
log.info("start");
String result1 = target.callA();//호출하는 메서드가 다름
log.info("result={}", result1);
//공통 로직1 종료
//공통 로직2 시작
log.info("start");
String result2 = target.callB();//호출하는 메서드가 다름
log.info("result={}", result2);
//공통 로직2 종료
}
/**
* Reflection을 사용.
* 클래스 정보를 획득하여 사용 예제
*/
@Test
void reflection1() throws Exception {
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
Object result1 = methodCallA.invoke(target);
log.info("result1={}", result1);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target);
log.info("rsult2={}", result2);
}
/**
* Reflection 을 사용해 더 동적으로 만들어보기
*/
@Test
void reflection2() throws Exception {
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
dynamicCall(methodCallA, target);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
dynamicCall(methodCallB, target);
}
private void dynamicCall(Method method, Object target) throws Exception{
log.info("start");
Object result = method.invoke(target);
log.info("result={}", result);
}
/**
* Runtime에 동작하기 때문에 컴파일 시점에 오류를 잡기 힘들다.
* 그래서 일반적으로 사용하지 않으면 좋다.
* 너무 일반적인 공통 처리가 필요할 때 부분적으로 주의해서 사용해야 한다.
*/
@Slf4j
static class Hello {
public String callA() {
log.info("callA");
return "A";
}
public String callB() {
log.info("callB");
return "B";
}
}
}
Reflection 주의사항
- Runtime에 동작하기 때문에 컴파일 시점에 오류를 잡기 힘들다.
- 그래서 일반적으로 사용하지 않으면 좋다.
- 너무 일반적인 공통 처리가 필요할 때 부분적으로 주의해서 사용해야 한다.
'개발언어 > JAVA' 카테고리의 다른 글
Java 9 ~ 17까지 도입할만한 유용한 기능과 예시 (0) | 2023.07.20 |
---|---|
[Spring AOP] 1.JDK Dynamic Proxy/CGLIB로 인터페이스 or 클래스 기반의 프록시 객체를 쉽게 생성하는 방법 (0) | 2022.11.16 |
[디자인 패턴] 프록시 패턴(Proxy Pattern), 데코레이터 패턴(Decorator Pattern) (0) | 2022.11.05 |
[디자인 패턴] 템플릿 메서드 패턴, 전략 패턴 (0) | 2022.11.03 |
동시성 문제 해결(ThreadLocal) (0) | 2022.08.31 |