Dailelog
자바 리플렉션 - reflection 본문
1. 리플렉션이란?
단어 자체는 반사, 반영이라는 의미를 가지고 있습니다. java에서는 아래의 의미를 가지고 있습니다.
Runtime 시에 이미 로드된 클래스 내에서 다른 클래스를 동적으로 로드하여 생성자, 필드, 메소드 등 access 할 수 있는 기능입니다.
C 같은 compile 언어들은 필드명, 메소드명 등의 정보를 주소값으로 만 알 수 있습니다. 그런데 java는 프로그램을 실행하면서 필드명, 메소드 명 등을 직접 알아 낼 수 있는 기능이 reflection입니다.
2. 사전 지식
- 정적 바인딩 vs 동적 바인딩
- 바인딩이란 프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정짓는 행위를 의미합니다.
- 정적 바인딩
- 실행 이전에 값이 확정되고 컴파일 타임에 호출될 함수가 결정되는 것으로, 함수는 기본적으로 정적 바인딩입니다.컴파일러는 선언되어있는 자료형을 보고 바인딩을 하기 때문에 실제로 가리키는 객체가 무엇이든 포인터의 자료형을 기반으로 호출의대상을 결정한다.
- 동적 바인딩
- 실행 이후 값이 확정 되고, 런타임에 호출될 함수가 결정나게 됩니다. virtual 키워드를 통해 동적 바인딩하는 함수를 가상 함수라고 합니다. 가상 함수가 선언 되면 포인터 변수가 실제로 가리키는 객체에 따라 호출의 대상이 결정됩니다.다형성의 원리가 적용될 수 있는 멤버 함수(메서드)로써 동적 바인딩으로 처리되는 메서드를 의미합니다. 동적 바인딩 수행 시 가상 메서드 테이블을 참조하여 매핑합니다.
- 가상 메서드 (Virtual Method)
- 정적 바인딩이 아닌 동적 바인딩으로 처리되기 때문에 컴파일 시점에 정해지지 않은, 실제로 존재하지 않는, 임시로 존재하는 메서드라는 의미로 가상 메서드라고 지칭합니다.
- Class Class
- 이 클래스의 역할은 어떤 클래스를 컴파일 하고 난 후 메모리 상에 로드를 하게 됩니다. c와 달리 클래스 자체에대한 정보를 자료구조화 시켜서 access 할 수 있게 만들어주는 클래스 입니다.
- Recode Classes그 정보가 바로 Recode Classes입니다. 위에서 이야기한 Class Class의 자료구조 화의 모습이 Parse tree 형태의 구조로 되어 클래스 데이터를가지고 있습니다. 이것을 Recode Classes 또는 Class recode라고 합니다.
- .class 파일이 bytecode로 이루어져 있습니다. 파일 내부에 컴파일 완료된 클래스의 정보가 담겨있습니다.
3. 실제 코드 예제
package je3.reflect;
import java.lang.reflect.*;
public class ShowClass {
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName(args[0]);
print_class(c);
}
public static void print_class(Class c)
{
if (c.isInterface()) {
System.out.print(Modifier.toString(c.getModifiers()) + " " +
typename(c));
}
else if (c.getSuperclass() != null) {
System.out.print(Modifier.toString(c.getModifiers()) + " class " +
typename(c) +
" extends " + typename(c.getSuperclass()));
}
else {
System.out.print(Modifier.toString(c.getModifiers()) + " class " +
typename(c));
}
Class[] interfaces = c.getInterfaces();
if ((interfaces != null)&& (interfaces.length > 0)) {
if (c.isInterface()) System.out.print(" extends ");
else System.out.print(" implements ");
for(int i = 0; i < interfaces.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(typename(interfaces[i]));
}
}
System.out.println(" {");
System.out.println(" // Constructors");
Constructor[] constructors = c.getDeclaredConstructors();
for(int i = 0; i < constructors.length; i++)
print_method_or_constructor(constructors[i]);
System.out.println(" // Fields");
Field[] fields = c.getDeclaredFields();
for(int i = 0; i < fields.length; i++)
print_field(fields[i]);
System.out.println(" // Methods");
Method[] methods = c.getDeclaredMethods();
for(int i = 0; i < methods.length; i++)
print_method_or_constructor(methods[i]);
System.out.println("}");
}
public static String typename(Class t) {
String brackets = "";
while(t.isArray()) {
brackets += "[]";
t = t.getComponentType();
}
String name = t.getName();
int pos = name.lastIndexOf('.');
if (pos != -1) name = name.substring(pos+1);
return name + brackets;
}
public static String modifiers(int m) {
if (m == 0) return "";
else return Modifier.toString(m) + " ";
}
public static void print_field(Field f) {
System.out.println(" " + modifiers(f.getModifiers()) +
typename(f.getType()) + " " + f.getName() + ";");
}
public static void print_method_or_constructor(Member member) {
Class returntype=null, parameters[], exceptions[];
if (member instanceof Method) {
Method m = (Method) member;
returntype = m.getReturnType();
parameters = m.getParameterTypes();
exceptions = m.getExceptionTypes();
System.out.print(" " + modifiers(member.getModifiers()) +
typename(returntype) + " " + member.getName() +
"(");
} else {
Constructor c = (Constructor) member;
parameters = c.getParameterTypes();
exceptions = c.getExceptionTypes();
System.out.print(" " + modifiers(member.getModifiers()) +
typename(c.getDeclaringClass()) + "(");
}
for(int i = 0; i < parameters.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(typename(parameters[i]));
}
System.out.print(")");
if (exceptions.length > 0) System.out.print(" throws ");
for(int i = 0; i < exceptions.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(typename(exceptions[i]));
}
System.out.println(";");
}
}
모든 객체에는 getClass()가 존재하고 객체 레퍼런스를 얻어와서 그것을 통해 리플렉션으로 클래스의 다양한 정보를 알아볼 수 있습니다.
단순히 정보를 알아내는 것 뿐 아니라 getMethods() 함수를 통해서 method가 컴파일된 결과값을 가지고 올수 있습니다. 그리고 아래의 코드처럼 활용할 수 있습니다.
Class c = Class.forname("main");
Method[] methods = c.getMethods();
methods[0].invoke(obj,agrs);
car.move(30,50);
methods[0].invoke(car,args); // args = 30,50
4. 리플렉션의 활용 사례
- commend 패턴에서 활용
- java.beans
- java.rmi
- 스프링 프레임워크에서 빈(Bean)의 생성과 의존성 주입, 어노테이션 기반의 설정 처리 등 (직접적으로 리플렉션 API를 사용하지 않아도 내부적으로 활용)
위 글은 동아리 테크톡에서 Reflection을 주제로 연사를 준비하던 중 예전 공부하였던 내용을 정리하기 위해 작성한 글입니다. JAVA EXAMPLES IN A NUTSHELL 책과 전공수업, java api document 참고하여 공부하였습니다.
LIST
'JAVA' 카테고리의 다른 글
우테코 프리코스 연습 3주차(프리프리코스) - Enum (0) | 2024.09.12 |
---|---|
일급 컬렉션(우테코 7기 프리코스 대비 6기 1주차) (0) | 2024.07.06 |
비밀번호 암호화 -SHA,BCrypt SCrypt, Argon2 (0) | 2024.06.20 |
JWT -JSON Web Tokens (0) | 2024.06.19 |
JAVA 프로그래밍 언어 플랫폼 내용 정리 (2) | 2024.06.08 |