728x90
아래 코드는 스프링을 사용한 BookService 코드이다.
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
}
질문
- bookRepository 인스턴스는 어떻게 null이 아닌걸까?
- 스프링은 어떻게 BookService 인스턴스에 BookRepository 인스턴스를 넣어준 것일까?
리플렉션 API - 클래스 정보 조회
리플렉션의 시작은 Class<T> 이다.
Class<T>에 접근하는 방법
- 모든 클래스를 로딩 한 다음 Class<T>의 인스턴스가 생긴다. “타입.class”로 접근할 수 있다.
- 모든 인스턴스는 getClass() 메소드를 가지고 있다. “인스턴스.getClass()”로 접근할 수 있다.
- 클래스를 문자열로 읽어오는 방법
아래 코드는 Class<T>에 접근하는 방법의 3가지 예시이다.
Class<Book> bookClass = Book.class;
Class<? extends Book> aClass = book.getClass();
Class<?> aClass1 = Class.forName("org.example.Book");
- 클래스패스에 해당 클래스가 없다면 ClassNotFoundException이 발생한다.
Class<T>를 통해 할 수 있는 것
- 필드 (목록) 가져오기
Book.class.getDeclaredFields()
- 메소드 (목록) 가져오기
MyBook.class.getDeclaredMethods();
- 상위 클래스 가져오기 , 인터페이스 (목록) 가져오기 , 애노테이션 가져오기(아래 설명), 생성자 가져오기 등등
- Modifires 사용 예시
Arrays.stream(Book.class.getDeclaredFields()).forEach(f -> {
int modifiers = f.getModifiers();
System.out.println(f);
System.out.println(Modifier.isPrivate(modifiers)); // Book 클래스 중 private 타입 가져온다.
System.out.println(Modifier.isStatic(modifiers)); // Book 클래스 중 static 타입 가져온다.
});
애노테이션
애노테이션은 원래 바이트코드를 로딩했을 때 메모리에는 남지 않는다.
근데 만약 메모리에 유지하고 싶다면 @Retention을 이용한다. → 리플렉션으로 조회 가능해진다.
애노테이션
- @Retention: 해당 애노테이션을 언제까지 유지할 것인가? 소스, 클래스, 런타임
- @Inherit: 해당 애노테이션을 하위 클래스까지 전달할 것인가?
- @Target: 어디에 사용할 수 있는가?
@Retention(RetentionPolicy.RUNTIME) // 런타임에서도 해당 에노테이션을 메모리에 저장
@Target({ElementType.TYPE, ElementType.FIELD}) // MyAnnotation 사용할 수 았는 위치 설정 (타입하고, 필드에만 붙일 수 있음)
@Inherited // 하위 클래스에 해당 애노테이션 전파
public @interface MyAnnotation {
String name() default "ksh";
int number() default 100;
}
리플렉션
- getAnnotations(): 상속받은 (@Inherit) 애노테이션까지 조회
- getDeclaredAnnotations(): 자기 자신에만 붙어있는 애노테이션 조회
리플렉션 API - 클래스 정보 수정 또는 실행
먼저 Book 클래스이다.
public class Book {
private String A = "A";
public static String B = "B";
public Book() {
}
public Book(String b) {
B = b;
}
public void c() {
System.out.println("C");
}
public int sum(int left, int right) {
return left + right;
정보 수정
Class<?> bookClass = Class.forName("org.example.Book"); // 클래스 로딩
// 기본 생성자 호출
Constructor<?> constructor = bookClass.getConstructor(null);
// 파라미터가 있는 생성자를 통해 새로운 인스턴스 할당
Constructor<?> constructor = bookClass.getConstructor(String.class);
//매개변수 값을 갖는 새로운 인스턴스 생성
Book book = (Book) constructor.newInstance("x");
System.out.println(book);
// B 필드 값을 가져온다.
Field b = Book.class.getDeclaredField("B");
// x
System.out.println(b.get(null));
// AAAAA로 필드 값을 세팅한다.
b.set(null, "AAAAA");
// AAAAA
System.out.println(b.get(null));
실행
Book 클래스에 한 메소드를 실행하는 코드이다.
Class<?> bookClass = Class.forName("org.example.Book"); // 클래스 로딩
// 기본 생성자 호출
Constructor<?> constructor = bookClass.getConstructor();
// 생성자를 통해 새로운 인스턴스 할당
Book book = (Book) constructor.newInstance();
// Book 클래스에 있는 sum 메소드를 불러온다.
Method c = Book.class.getDeclaredMethod("sum",int.class, int.class);
// invoke을 통해 sum 메소드 실행
int invoke = (int) c.invoke(book1,1,2);
// 값은 3이 나온다.
System.out.println(invoke); // 3
리플렉션 정리
리플렉션 API를 간단히 말하면, 클래스에 접근 가능한 API이다.
리플렉션 사용시 주의사항
- 지나친 사용은 성능 이슈를 야기할 수 있다. 반드시 필요한 경우에만 사용!
- 컴파일 타입에 확인되지 않고, 런차임 시에만 발생하는 문제를 만들 가능성이 있다.
- 접근 지시자를 무시할 수 있다.
리플렉션 사용하고 있는 라이브러리
스프링
- 의존성 주입
- MVC 뷰에서 넘어온 데이터를 객체에 바인딩 할 때
하이버네이트
- @Entity 클래스에 Setter가 없다면 리플렉션을 사용한다.
Junit 등등 사용되고 있다.
'아무거나 개발공부 > JAVA 기초다지기' 카테고리의 다른 글
람다에서 지역변수 값을 변경하지 못하는 이유 Variable used in lambda expression should be final or effectively final (0) | 2024.01.08 |
---|---|
JVM & 클래스 로더란? (0) | 2023.11.21 |
직렬화 매개변수로 ArrayList는 되고, List는 안되는 이유 (0) | 2023.10.05 |
JAVA - UnsupportedOperationException 발생 해결 (0) | 2023.06.26 |
Arrays.asList() vs Collection.singletonList() vs List.of() (1) | 2022.12.08 |