Java & Spring

[Java] Reflection

nippycloud 2026. 1. 1. 18:27

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