본문 바로가기

Java/Java

(JAVA) 리플렉션 API(1) - 클래스 정보 조회

@RunWith(SpringRunner.class)
@SpringBootTest
public class BookServiceTest {
    @Autowired
    BookService bookService;

    @Test
    public void di() {
        Assert.assertNotNull(bookService.bookRepository);
        Assert.assertNotNull(bookService);
    }
}

위의 코드를 실행하였을때 @Autowired 어노테이션을 썻다는것 만으로 BookService 객체를 생성하지 않더라도 NULL 값이 발생하지 않는다.

이게 어떻게 가능한일 일까?


 

리플렉션의 시작은 Class<T>

 

Class (Java Platform SE 8 )

Determines if the specified Class object represents a primitive type. There are nine predefined Class objects to represent the eight primitive types and void. These are created by the Java Virtual Machine, and have the same names as the primitive types tha

docs.oracle.com

 

Class<T>에 접근하는 방법

  • 모든 클래스를 로딩 한 다음 Class<T>의 인스턴스가 생긴다. “타입.class”로 접근할 수 있다.

  • 모든 인스턴스는 getClass() 메소드를 가지고 있다. “인스턴스.getClass()”로 접근할 수 있다.

  • 클래스를 문자열로 읽어오는 방법

    • Class.forName(“FQCN”)

    • 클래스패스에 해당 클래스가 없다면 ClassNotFoundException이 발생한다.

 

Book 클래스 생성

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

    public void g() {
        System.out.println("g");
    }

    public int h() {
        return 100;
    }
}

클래스로딩이 끝나면 클래스타입의 인스턴스를 만들어서 Heap 메모리 영역에 올린다. (클래스로딩은 런타임 시점)

 

클래스타입의 인스턴스를 가져오는 방법

 

방법1. 클래스타입에 직접 가져올때

Class<Book> bookClass = Book.class;

 

방법2. 인스턴트를 통할때

Book book = new Book();
Class<? extends Book> bookClass1 = book.getClass();

방법3. 문자열로 접근할 때

Class<?> aClass = Class.forName("me.choi.Book");

 

 

getFields : public 한 필드를 리턴

Arrays.stream(bookClass.getFields()).forEach(System.out::println);

d 밖에 안나오는 이유는

d만 public한 필드이기 때문이다.

 

 

 

getDeclaredFields : 접근제어자에 상관없이 모든 필드를 리턴

 

Arrays.stream(bookClass.getDeclaredFields()).forEach(System.out::println);

 

 

 

 

 

 

접근제어자 무시 (with setAccessible(true))

Arrays.stream(bookClass.getDeclaredFields()).forEach(f -> {
  try {
  	System.out.printf("%s %s \n",f, f.get(book));
  } catch (IllegalAccessException e) {
  	e.printStackTrace();
  }
});

위의 코드를 작성하면 에러 발생!

private 접근제어자에 접근을 할 수 없기 떄문에

 

Arrays.stream(bookClass.getDeclaredFields()).forEach(f -> {
  try {
  	f.setAccessible(true);
  	System.out.printf("%s %s \n",f, f.get(book));
  } catch (IllegalAccessException e) {
  	e.printStackTrace();
  }
});

실행결과

getMethods : 모든 메서드 불러오는 방법

Arrays.stream(bookClass.getMethods()).forEach(
	System.out::println
);

실행결과

getDeclaredConstructors : 모든 생성자 불러오는 방법

 

Arrays.stream(bookClass.getDeclaredConstructors()).forEach(System.out::println);

실행결과

 

getInterfaces : 모든 인터페이스 불러오는 방법

Arrays.stream(MyBook.class.getInterfaces()).forEach(System.out::println);

실행결과

 

기타기능

Arrays.stream(bookClass.getDeclaredFields()).forEach(f ->{
    int modifier = f.getModifiers();
    //접근제어자 확인
    System.out.println(Modifier.isPublic(modifier));
    System.out.println(Modifier.isPrivate(modifier));
    System.out.println(Modifier.isStatic(modifier));
    System.out.println(Modifier.isAbstract(modifier));
    //리턴타입 확인
    System.out.println(f.getReturnType());
});