Reflection = 반사
프로그램이 실행 중에 자기 자신의 구조를 들여다보고 그 구조를 변경, 조작
클래스의 정보를 런타임에 읽어서 활용
클래스 메타 데이터 (클래스명, 접근 제어자, 상위 클래스, 구현된 인터페이스 등), 필드, 메서드, 생성자 정보를 확인할 수 있다.
스프링 DI, IoC의 원리 : Java Reflection
직접 만든 클래스 (~Service, controller, repository ..)를 @Bean, @Component 등으로 스프링 프레임워크가 대신 빈으로 등록
@Bean, @Component 은 내부적으로 자바 리플렉션 기술을 사용한다.
리플렉션을 이용하기 위한 기본적인 클래스 BasicData
package reflection;
public class BasicData {
public String publicField;
private int privateField;
public BasicData() {
System.out.println("BasicData.BasicData");
}
private BasicData(String data) {
System.out.println("BasicData.BasicData : " + data);
}
public void call() {
System.out.println("BasicData.call");
}
public String hello(String str) {
System.out.println("BasicData.hello");
return str + " hello";
}
private void privateMethod() {
System.out.println("BasicData.privateMethod");
}
void defaultMethod() {
System.out.println("BasicData.defaultMethod");
}
protected void protectedMethod() {
System.out.println("BasicData.protectedMethod");
}
}
리플렉션의 기능을 이용한 BasicMain
package reflection;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class BasicMain {
public static void main(String[] args) throws ClassNotFoundException {
// 클래스명.class 로 메타 데이터 조회
Class<BasicData> basicDataClass1 = BasicData.class;
System.out.println("basicDataClass1 = " + basicDataClass1);
// 객체.getClass() 로 메타 데이터 조회
BasicData basicData = new BasicData();
Class<? extends BasicData> basicDataClass2 = basicData.getClass();
System.out.println("basicDataClass2 = " + basicDataClass2);
// Class<?> basicDataClass = Class.forName(className), String 변수 (사용자 입력도 가능)로 메타 데이터 조회
String className = "reflection.BasicData";
Class<?> basicDataClass3 = Class.forName(className);
System.out.println("basicDataClass3 = " + basicDataClass3);
// 리플렉션을 활용해 정보 조회
Class<BasicData> basic = BasicData.class;
System.out.println("basicData.getName() = " + basic.getName());
System.out.println("basicData.getSimpleName() = " + basic.getSimpleName());
System.out.println("basicData.getPackage() = " + basic.getPackage());
System.out.println("basicData.getSuperclass() = " + basic.getSuperclass());
System.out.println("basicData.getInterfaces() = " + Arrays.toString(basic.getInterfaces()));
System.out.println("basicData.isInterface() = " + basic.isInterface());
System.out.println("basicData.isEnum() = " + basic.isEnum());
System.out.println("basicData.isAnnotation() = " + basic.isAnnotation());
// 수정자 조회, basic 은 클래스에서 조회했기에 public 하나만 카운트, public Class BasicData
int modifiers = basic.getModifiers();
System.out.println("basicData.getModifiers() = " + modifiers);
System.out.println("isPublic = " + Modifier.isPublic(modifiers));
System.out.println("Modifier.toString() = " + Modifier.toString(modifiers));
}
}
리플렉션을 활용하여 런타임에 메서드 정보 변경
package reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodReflect {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 클래스로 리플렉션 사용
Class<BasicData> basicDataClass = BasicData.class;
// getMethods : 상위 클래스 메서드까지 모두 조회, public만 조회
Method[] methods = basicDataClass.getMethods();
for (Method method : methods) {
System.out.println("method = " + method);
}
// getDeclaredMethods : 현재 클래스 메서드만 조회, private만 조회
Method[] declaredMethods = basicDataClass.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println("method = " + method);
}
//---------------------------------------------------------------------------------
// 객체로 리플렉션 사용
BasicData basicData = new BasicData();
Class<? extends BasicData> basicDataInstance = basicData.getClass();
String methodName = "hello"; // hello 라는 메서드 조회 준비
// getDeclaredMethod 로 파라미터가 String인 hello 메서드 조회
Method method1 = basicDataInstance.getDeclaredMethod(methodName, String.class);
Object returnValue = method1.invoke(basicData, "test"); // String 파라미터에 "test" 전달
System.out.println("returnValue = " + returnValue); // hello 메서드에 test 파라미터를 전달하여 런타임에 실행
}
}
리플렉션을 활용하여 런타임에 필드 정보 변경
package reflection;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.lang.reflect.Field;
public class FiledReflect {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class<BasicData> basicDataClass = BasicData.class;
Field[] fields = basicDataClass.getFields();
for (Field field : fields) {
System.out.println("field = " + field);
}
Field[] declaredFields = basicDataClass.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println("declaredFields = " + field);
}
//---------------------------------------------------------------------------------
User user = new User("id1,", "userA", 20);
System.out.println("기존 이름 : " + user.getName());
// 인스턴스로 메타 데이터 조회
Class<? extends User> aClass = user.getClass();
Field nameField = aClass.getDeclaredField("name"); // name 필드 런타임에 수정
nameField.setAccessible(true); // private에 리플렉션으로 접근 허용
nameField.set(user, "userB"); // 기존 userA를 userB로 런타임에 변경
System.out.println("변경 이름 : " + user.getName());
}
@Data
@AllArgsConstructor
static class User {
private String id;
private String name;
private Integer age;
}
}
리플렉션을 활용하여 런타임에 생성자 정보 변경
package reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ConstructorReflect {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<BasicData> basicDataClass = BasicData.class;
// Class<?> basicDataClass = Class.forName("reflection.BasicData"); 파라미터로 패키지, 클래스명 입력으로도 선언 가능
Constructor<?>[] constructors = basicDataClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("constructor = " + constructor);
}
Constructor<?>[] declaredConstructors = basicDataClass.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println("declaredConstructors = " + constructor);
}
//---------------------------------------------------------------------------------
// private BasicData (String data) { ... } 생성자를 리플렉션으로 런타임에 수정
Constructor<?> constructor = basicDataClass.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object instance = constructor.newInstance("hello"); // String data에 hello 파라미터 전달
System.out.println("instance = " + instance);
Method method = basicDataClass.getDeclaredMethod("call"); // call 메서드 조회
method.invoke(instance); // hello 파라미터를 가진 BasicData 객체가 call 메서드 수행
}
}
'Java & Spring' 카테고리의 다른 글
| [Servlet] WebSocket (0) | 2026.02.16 |
|---|---|
| [Spring] Security (0) | 2026.02.15 |
| [JAVA] JVM의 구조 (0) | 2025.12.09 |
| [Servlet] File Upload (0) | 2025.11.16 |
| MyBatis (0) | 2025.11.15 |