리플렉션은 힙 영역에 로드된(런타임) Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API이다.
public static void main(String[] args) {
// 클래스 리터럴(*.class)로 얻기
Class<? extends String> cls2 = String.class;
System.out.println(cls2); // class java.lang.String
}
public static void main(String[] args) {
// 스트링 클래스 인스턴스화
String str = new String("Class클래스 테스트");
// getClass() 메서드로 얻기
Class<? extends String> cls = str.getClass();
System.out.println(cls); // class java.lang.String
}
public static void main(String[] args) {
try {
// 도메인.클래스명으로 얻기
Class<?> cls3 = Class.forName("java.lang.String");
System.out.println(cls3); // class java.lang.String
} catch (ClassNotFoundException e) {}
}
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("비밀번호는 1234입니다.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class<Member> memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("제이온", 23, "다라쓰 개발");
Class<? extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class<?> memberClass3 = Class.forName("{패키지명}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// 실행 결과
1740000325
1740000325
public class Main {
public static void main(String[] args) throws Exception {
// Member의 모든 생성자 출력
Member member = new Member();
Class<? extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Member의 기본 생성자를 통한 인스턴스 생성
Constructor<? extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Member의 다른 생성자를 통한 인스턴스 생성
Constructor<? extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("제이온", 23, "다라쓰 개발");
System.out.println("member3 = " + member3);
}
}
// 실행 결과
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
member3 = Member{name='제이온', age=23, hobby='다라쓰 개발'}
setAccessible(true)
memberClass.getDeclaredMethod("speak", String.class);
마지막으로 invoke() 메소드를 통해 리플렉션 API 얻어 온 메소드를 호출 할 수 있다.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("제이온", 23, "다라쓰 개발");
Class<? extends Member> memberClass = member.getClass();
// 필드 접근
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "제이온2");
System.out.println(member);
// 메소드 접근
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "리플렉션 테스트");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// 실행 결과
제이온
23
다라쓰 개발
Member{name='제이온2', age=23, hobby='다라쓰 개발'}
리플렉션 테스트
비밀번호는 1234입니다.
Spring의 Bean Factory를 보면, @Controller, @Service, @Repository 등의 어노테이션만 붙이면 Bean Factory에서 알아서 해당 어노테이션이 붙은 클래스를 생성하고 관리해 주는 것을 알 수 있다.
개발자가 Bean Factory에 알려준 적이 없는데 이것이 가능한 이유는 바로 리플렉션 때문이다.
런타임에 해당 어노테이션이 붙은 클래스를 탐색하고 발견하면 리플렉션을 통해 해당 클래스의 인스턴스를 생성하고 필요한 필드를 주입하여 BeanFactory에 저장하는 식으로 사용된다.
단, 캡슐화를 저해하므로 꼭 필요한 상황에서만 사용하는 것이 좋다.
참고
- https://gyrfalcon.tistory.com/entry/Java-Reflection
- https://steady-coding.tistory.com/609
- https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EB%88%84%EA%B5%AC%EB%82%98-%EC%89%BD%EA%B2%8C-%EB%B0%B0%EC%9A%B0%EB%8A%94-Reflection-API-%EC%82%AC%EC%9A%A9%EB%B2%95#%EC%9E%90%EB%B0%94_%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98_%EC%82%AC%EC%9A%A9%EB%B2%95