[Java] 리플렉션 - 클래스 정보 조회, 애노테이션과 리플렉션

YulHee Kim·2021년 12월 22일
0

Java

목록 보기
8/8

백기선님의 더 자바, 코드를 조작하는 다양한 방법을 수강한 후 정리해보았습니다.

[ 리플렉션 ]

💡 스프링의 Dependency Injection은 어떻게 동작할까?

내부적으로 어떻게 동작할지 알아보겠습니다.

BookRepository를 생성한 후 BookService도 생성해보겠습니다.

BookService


@Service
public class BookService {

    @Autowired
    BookRepository bookRepository;
}

BookServiceTest

@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이 아닌 걸까요?? 이제부터 알아보겠습니다. 😎

💡 리플렉션 API 1부: 클래스 정보 조회

리플렉션(Reflection)이란?
리플렉션은 구체적인 클래스 타입을 알지 못해서 그 클래스의 메소드와 타입 그리고 변수들을 접근할 수 있도록 해주는 자바 API입니다.

Book.class

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;
    }

}

MyBook

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"로 접근할 수 있습니다.
  • 모든 인스턴스는 getClass() 메소드를 가지고 있습니다. "인스턴스.getClass()"로 접근할 수 있습니다.
  • 클래스를 문자열로 읽어오는 방법
    • Class.forName("FQCN")
    • 클래스패스에 해당 클래스가 없다면 ClassNotFoundException이 발생합니다.

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: 어디에 사용할 수 있는가?

리플렉션

  • getAnnotations(): 상속받은 (@Inherit) 애노테이션까지 조회
  • getDeclaredAnnotations(): 자기 자신에게만 붙어있는 애노테이션 조회

참고)
https://kingname.tistory.com/164

profile
백엔드 개발자

0개의 댓글