- 프로그래머스
- 데이터베이스
- jpa
- 1차원 배열
- 스프링
- Django
- spring
- spring mvc
- 스프링부트
- 문자열
- mysql
- SSL
- 자바
- nginx
- springboot
- sql
- AWS
- select
- spring boot
- session
- Git
- @transactional
- java
- spring security 6
- Docker
- DI
- ORM
- join
- PYTHON
- string
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
개발하는 자몽
리플렉션(Reflection) 본문
섹션 5. 동적 프록시 기술 - 리플렉션
리플렉션에 대해 정리해보자.
JDK 동적 프록시를 이해하기 위해서는 리플렉션(Reflection)을 알아야 한다. 이 기술은 JDK 동적 프록시와 마찬가지로 자바에서 제공한다.
리플렉션을 이용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드도 동적으로 호출할 수 있다.
@Slf4j
public class ReflectionTest {
@Test
void reflection0() {
Hello target = new Hello();
//공통 로직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 종료
}
...
}
Hello라는 클래스가 있고 해당 클래스에는 callA(), callB()라는 메서드가 있다고 하자. 위 코드는 두 메서드를 호출하는 부분을 제외하면 동일한 로직을 가지고 있다. 이러한 동일한 로직을 리플렉션을 통해 동적으로 호출할 수 있다.
@Slf4j
public class ReflectionTest {
...
@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); //target 인스턴스에 있는 callA()를 호출한다.
log.info("result1={}", result1);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target); //target 인스턴스에 있는 callB()를 호출한다.
log.info("result2={}", result2);
}
...
}
Class.forName()을 이용하여 호출할 메서드가 속한 클래스의 정보를 가져온다. forName()에 클래스 이름을 넘길 때, 내부 클래스는 $를 이용하여 표시한다.
호출할 메서드의 정보는 위에서 획득한 클래스의 정보를 이용하여 가져온다 : getMethod("호출할 메서드 명"). 이렇게 가져온 메서드 정보는 Method라는 클래스로 추상화된다.
마지막으로 클래스 인스턴스(target, forName()의 대상)에 있는 메서드를 호출한다. 호출한 결과는 Object로 반환되는데, 이는 최상위 클래스이므로 어떤 인스턴스라도 받을 수 있다.
위 코드에서 호출할 메서드의 정보는 Method라는 클래스로 추상화되고, Object가 최상위 클래스라는 점을 떠올려보자. 해당 사실을 통해 위 코드를 아래처럼 작성할 수 있다.
@Slf4j
public class ReflectionTest {
...
@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);
}
...
}
결과적으로 정적인 target.callA() , target.callB() 코드를 리플렉션을 사용해서 Method라는 메타정보로 추상화했다. 덕분에 공통 로직을 만들 수 있게 되었다.
리플렉션을 이용하여 동적으로 코드를 호출하는 등 공통 로직을 만들 수 있게 되었지만, 런타임 시 이루어지기 때문에 컴파일 때 오류가 잡히지 않는다. 이 점을 유의하고 정말 필요한 때에 사용해야 한다.
'Java' 카테고리의 다른 글
Collections, Collector, stream의 함수 (0) | 2023.05.03 |
---|---|
[Tomcat Error] java.lang.IllegalArgumentException: The main resource set specified [...\tomcat\tomcat.8080/webapps] is not valid (0) | 2023.04.12 |
동시성 문제와 쓰레드 로컬 (0) | 2023.03.22 |
Bean Validation - HTTP 메시지 컨버터 (0) | 2023.03.13 |
Bean Validation - 에러 코드 (0) | 2023.03.10 |