백기선님의 더 자바, 코드를 조작하는 다양한 방법을 수강한 후 정리해보았습니다.
내부적으로 어떻게 동작할지 알아보겠습니다.
BookRepository를 생성한 후 BookService도 생성해보겠습니다.
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class BookServiceTest {
@Autowired BookService bookService;
@Test
public void di() {
Assert.assertNotNull(bookService);
Assert.assertNotNull(bookService.bookRepository);
}
}
bookService의 bookRepository가 null이 아닌지 확인함으로써 스프링이 bookRepository라는 bean을 만들어서 bookService에 주입시켜줬다는 것을 알 수 있습니다.
도대체 위 필드들이 어떻게 Null이 아닌 걸까요?? 이제부터 알아보겠습니다. 😎
리플렉션(Reflection)이란?
리플렉션은 구체적인 클래스 타입을 알지 못해서 그 클래스의 메소드와 타입 그리고 변수들을 접근할 수 있도록 해주는 자바 API입니다.
public class Book {
private static String B = "BOOK";
private static final String C = "BOOK";
private String a = "a";
public String d = "d";
protected String e = "e";
public Book() {
}
public Book(String a, String d, String e) {
this.a = a;
this.d = d;
this.e = e;
}
private void f() {
System.out.println("f");
}
private void g() {
System.out.println("g");
}
public int h() {
return 100;
}
}
Book을 상속받고 MyInterface를 만들어보겠습니다.
public class MyBook extends Book implements MyInterface {
}
이 정보들을 접근하려면 Class 타입이 있어야합니다. Book.class
같은 경우 클래스 로딩이 끝나면 클래스 타입의 인스턴스를 만들어서 힙에다 넣어줍니다. 클래스를 로딩만해도 인스턴스를 만들어주므로 인스턴스에 접근할 수 있습니다.
타입을 직접 지정해주어서 접근하는 방법 Class<Book> bookClass = Book.class;
도 있고, get()함수를 이용해서 접근하는 방법 book.getClass();
도 있습니다.
클래스 인스턴스에 접근했을 때 참조할 수 있는 여러가지 방법들이있습니다. 예시로 bookClass의 필드들을 가져와보겠습니다.
public class App {
public static void main(String[] args) throws ClassNotFoundException {
Class<Book> bookClass = Book.class;
Arrays.stream(bookClass.getDeclaredFields()).forEach(System.out::println);
}
}
슈퍼 클래스도 확인할 수 있습니다. 결과는 Book이라는 클래스가 나옵니다.
public class App {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(MyBook.class.getSuperclass());
}
}
인터페이스도 확인해보겠습니다. MyInterface가 출력됩니다.
public class App {
public static void main(String[] args) throws ClassNotFoundException {
Arrays.stream(MyBook.class.getInterface()).forEach(System.out::println);
}
}
등등
리플렉션을 사용하면 여러가지 정보를 참조할 수 있습니다.
Class<T>
리플렉션의 시작은 Class<T>
입니다.
Class<T>
에 접근하는 방법Class<T>
의 인스턴스가 생깁니다. "타입.class"로 접근할 수 있습니다.Class<T>
를 통해 할 수 있는 것
- 필드 (목록) 가져오기
- 메소드 (목록) 가져오기
- 상위 클래스 가져오기
- 인터페이스 (목록) 가져오기
- 애노테이션 가져오기
- 생성자 가져오기
등등
자바에서 애노테이션을 만들 수 있습니다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
String name() default "yulhee";
int number() default 100;
}
애노테이션은 이 애노테이션을 사용할 수 있는 위치를 지정할 수 있습니다. 붙일 수 있는 위치를 제한하고 싶다면, @Target({ElementType.TYPE, ElementType.FIELD})
이 애노테이션을 사용하면 타입하고 필드에만 붙일 수 있습니다. 생성자같은 곳에는 붙일 수 없게 됩니다.
또한 애노테이션은 어떤 제한된 값들을 가질 수 있고 기본값을 설정할 수 있습니다. 기본값을 설정해주지 않으면 애노테이션을 사용할 때 값을 일일이 넣어주어야합니다.
- @Retention: 해당 애노테이션을 언제까찌 유지할 것인가? 소스, 클래스, 런타임
- @Inherit: 해당 애노테이션을 하위 클래스까지 전달할 것인가?
- @Target: 어디에 사용할 수 있는가?