구체적인 클래스 타입을 알지 못해도, 그 클래스의 메소드, 타입, 변수들을 접근할 수 있도록 해주는 자바 API
컴파일 언어(정적인 언어)인 자바에 없는 동적으로 객체를 생성하는 기술을 Reflection으로 대신한다.
✋ 정적인 언어 : Compile 시점에 타입을 결정하는 언어 (Java, C/C++)
✋ 동적인 언어 : Runtime 시점에 타입을 결정하는 언어 (Javascript, Python)
✍ 확장성 특징
어플리케이션이 정규화된 이름을 사용하게 하여 확장성 객체의 인스턴스를 생성하여 외부 사용자 정의 클래스를 사용할 수 있게 한다.
✍ 클래스 브라우저 및 시각적 개발 환경을 제공
클래스의 Method, Property, Constructor를 열거할 수 있고, 개발자가 올바른 코드를 작성하는데 도움이 되도록 Reflection에서 사용할 수 있는 형식 정보를 제공한다.
✍ 디버거 및 테스트 도구
개인 Property, Method, Constructor를 검사
✍ 오버헤드
Reflection에는 동적으로 해석되는 유형이 있기에 특정 JVM 최적화를 수행할 수 없다. 그 이유로 비 Reflection 작업보다 성능면에서 떨어진다.
✍ 보안 제한 사항
✍ 캡슐화 저해
비 Reflection 코드에서 작동하지 않는 코드 (private한 Property및 Method에 액세스)를 수행하게되어 예기치 않은 부작용을 발생시킬 수 있고, 추상화를 꺠뜨릴 수 있다.
- JVM에서 실행되는 어플리케이션의
런타임 동작을 검사하거나 수정
할 수 있는 기능이 필요한 프로그램에서 사용된다. 즉, 동적으로 클래스를 사용해야 할때 필요하다.- Reflection은 애플리케이션 개발에서보다는 프레임워크, 라이브러리에서 많이 사용된다.
✋ Class.getClass()
를 통해 클래스의 정보 로드하기
상속된 클래스를 포함하여 모든 공용 클래스, 인터페이스 및 열거형을 반환
//임의의 클래스를 가져오는 방법
Class c = "foo".getClass();
System.out.println(c); //class java.lang.String
//Array는 객체이므로 Array 인스턴스에서 클래스 정보를 로드할 수 있습니다.
byte[] b = new byte[1024];
Class c1 = b.getClass();
System.out.println(c1); //class [B
Set<String> s = new HashSet<>();
Class c2 = s.getClass();
System.out.println(c2); //class java.util.HashSet
✋ Primitive Type.class
를 통해 정보 로드하기
- Primitive Type(원시타입)
- 다차원 배열
- java.io Type
boolean bl;
Class c3 = bl.getClass(); //컴파일 에러 발생
Class c4 = boolean.class;
Class c5 = java.io.PrintStream.class;
System.out.println(c5); //class java.io.PrintStream
Class c6 = int[][].class;
System.out.println(c6); //class [[I
✋ TYPE 필드
로 Reference Type(참조타입)의 원시형 클래스 반환하기
Class c8 = Double.TYPE; //double
Class c9 = Void.TYPE; //void
✋ Class.forName()
으로 클래스 로드하기
//아래와 같이 패키지 명으로 클래스를 로드할 수 있습니다.
Class c7 = Class.forName("ko.maeng.reflection.ReflectionApplication");
Class doubleArray = Class.forName("[D"); //class [D
Class stringArray = Class.forName("[[Ljava.lang.String;"); //class [Ljava.lang.String;
✋ .isInstance()
메소드로 인스턴스 확인하기
try{
Class c = Class.forName("ko.maeng.reflection.A");
boolean b = c.isInstance(new Integer(22));
System.out.println(b); //false
boolean b1 = c.isInstance(new A());
System.out.println(b1); //true
} catch (ClassNotFoundException e){
e.printStackTrace();
}
@Controller
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
....
@PostMapping
public String write(UserSession userSession, ArticleDto.Request articleDto){
...
}
@GetMapping("/{id}")
public String show(@PathVariable int id, Model model) {
...
}
}
✋ @Controller
를 넣어주면 인스턴스를 생성 하지 않아도 스프링이 알아서 생성해서 빈으로 관리해준다.
- 의문
- 스프링은 ArticleController의 존재를 어떻게 알고 만들어줄까?
- ArticleService 라는 필드는 어떻게 주입해줄까?
- 모든 메소드의 파라미터 개수, 타입이 다른데 어떻게 알고 해당하는 값을 바인딩 해줄까?
👉 스프링은 ArticleController의 정보를 알아내기 위해서
Reflection
을 사용한다