인프런 백기선님의 더 자바, 코드를 조작하는 다양한 방법 강의 정리

크리링·2022년 8월 31일
0

백기선 - 더 자바 코드를 조작하는 방법

1부. 자바

1. 자바, JVM, JDK, JRE

Public class HelloJava{
	public static  void main(String args[]){
		System.out.println(Hello world”);
	}
}
  • 자바 컴파일 후 생성된 .class 파일
    - 바이트 코드
    - 이후 인터프리터를 통해 10101 식으로 기계어로 바뀌어 실행

JVM(Java Virtual Machine)

  • 자바 가상 머신으로 자바 바이트 코드(.class 파일)을 OS에 특화된 코드로 변환 (인터프리터와 JIT 컴파일러)하여 실행한다.
  • 바이트 코드를 실행하는 표준(JVM 자체는 표준)이자 구현체(특정 벤더가 구현한 JVM)다.
  • JVM 벤더 : 오라클, 아마존, Azul, …
  • 특정 플랫폼에 종속적

JRE(Java Runtime Environment)

  • 자바 애플리케이션을 실행할 수 있도록 구성된 배포판
  • JVM과 핵심 라이브러리 및 자바 런타임 환경에서 사용하는 프로퍼티 세팅이나 리소스 파일을 가지고 있다.
  • 개발 관련 도구는 포함하지 않는다.(그건 JDK에서 제공)

JDK(Java Development Kit)

  • JRE + 개발에 필요한 툴
  • 소스 코드를 작성할 때 사용하는 자바 언어는 플랫폼에 독립적
  • 오라클은 자바 11 이후 JDK만 제공해 JRE를 따로 제공하지 않음

자바

  • 프로그래밍 언어
  • JDK에 들어있는 자바 컴파일러(javac)를 사용하여 바이트코드로 컴파일 할 수 있다.
  • 자바 유료화? 오라클에서 만든 Oracle JDK 11 버전부터 상용으로 사용할 때 유료

JVM 언어

  • JVM 기반으로 동작하는 프로그래밍 언어
  • 클로저, 그루비, Ruby, Jython, Kotlin, …

2. JVM 구조

클래스 로더 시스템

  • .class 에서 바이트코드를 읽고 메모리에 저장

  • 로딩 : 클래스 읽어오는 과정

  • 링크 : 레퍼런스를 연결하는 과정

  • 초기화 : static 값들 초기화 및 변수에 할당

메모리

  • 메소드 영역 : 클래스 수준의 정보 (클래스 이름, 부모 클래스 이름, 메소드, 변수) 저장. 공유 자원
  • 힙 : 객체를 저장. 공유 자원이다.
  • 스택 : 쓰레드마다 런타임 스택을 만들고, 그 안에 메소드 호출을 스택 프레임이라 부르는 블럭으로 쌓는다. 쓰레드 종료하면 런타임 스택도 사라진다.
  • PC 레지스터 : 쓰레드마다 쓰레드 내 현재 실행할 instruction의 위치를 가리키는 포인터가 생성된다.
  • 네이티브 메소드 스택

실행 엔진

  • 인터프리터 : 바이트 코드를 한 줄 씩 실행
  • JIT 컴파일러 : 인터프리터 효율을 높이기 위해, 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러로 반복되는 코드를 모두 네이티브 코드로 바꿔둔다. 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용
  • GC : 더이상 참조되지 않는 객체를 모아서 정리한다.

JNI(Java Native Interface)

  • 자바 애플리케이션에서 C, C++, 어셈블리로 작성된 함수를 사용할 수 있는 방법을 제공

3. 클래스 로더 시스템

클래스 로더

  • 로딩, 링크, 초기화 순으로 진행
  • 로딩
    - 클래스 로더가 .class 파일을 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고 “메소드” 영역에 저장
    - 이때 메소드 영역에 저장하는 데이터
    		- FQCN
    		- 클래스 | 인터페이스 | 이늄
    		- 메소드와 변수
      
    	- 로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 “힙”영역에 저장
  • 링크
    - Verify, Prepare, Resolve(optional) 세 단계
    - 검증 : .class 파일 형식 유효한지 체크
    - Preparation : 클래스 변수 (static 변수)와 기본값에 필요한 메모리
    - Resolve : 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체

  • 초기화
    - Static 변수의 값을 할당 (static 블럭이 있다면 이때 실행)
  • 클래스 로더는 계층 구조로 이뤄져 있으면 기본적으로 세가지 클래스 로더가 제공된다.
    - 부트 스트랩 클래스 로더 -  JAVA_HOME\lib에 있는 코어 자바 API를 제공한다. 최상위 우선순위를 가진 클래스 로더
    - 플랫폼 클래스로더 - JAVA_HOME\lib\ext 폴더 또는 java.ext.dirs 시스템 변수에 해당하는 위치에 있는 클래스를 읽는다.
    - 애플리케이션 클래스로더 - 애플리케이션 클래스패스(애플리케이션 실행할 때 주는 -classpath 옵션 또는 java.class.path 환경 변수의 값에 해당하는 위치)에서 클래스를 읽는다.

리플렉션

리플렉션은 어셈블리, 모듈 및 형식을 설명하는 개체(Type 형식)를 제공합니다. 리플렉션을 사용하면 동적으로 형식 인스턴스를 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져와 해당 메서드를 호출하거나, 필드 및 속성에 액세스할 수 있습니다.

클래스 정보 조회

  • Class에 접근하는 방법
    - 모든 클래스를 로딩한 다음 Class의 인스턴스가 생긴다. “타입.class”로 접근할 수 있다.
    - 모든 인스턴스는 getClass()메소드를 가지고 있다. “인스턴스.getClass()”로 접근할 수 있다.
    - 클래스를 문자열로 읽어오는 방법

    		- Class.forName(“FQCN”)
    		- 클래스패스에 해당 클래스가 없다면 

    ClassNotFoundException이 발생한다.


애노테이션과 리플렉션

애노테이션은 기본적으로 주석과 마찬가지 (추가 기능 존재)

  • 중요 애노테이션
    - @Retention : 해당 애노테이션을 언제까지 유지할 것인가? 소스, 클래스, 런타임
    - @Inherit : 해당 애노테이션을 하위 클래스까지 전달할 것인가?
    - @Target : 어디에 사용할 수 있는가?

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


클래스 정보 수정 또는 실행

  • Class 인스턴스 만들기
    - Class.newInstance()는 deprecated 됐으며
    - 생성자를 통해서 만들어야 한다.
  • 생성자로 인스턴스 만들기
    - Constructor.newInstance(params)
  • 필드 값 접근하기/설정하기
    - 특정 인스턴스가 가지고 있는 값을 가져오는 것이기 때문에 인스턴스가 필요하다.
    - Field.get(object)
    - Field.set(object, value)
    - Static 필드를 가져올 때는 object가 없어도 되니까 null을 넘기면 된다.
  • 메소드 실행하기
    - Object Method.invoke(object, params)

리플렉션 정리 및 활용

  • 리플렉션 장점
    - 클래스에 문자열만 존재해도 클래스 생성과 메소드 실행 가능
    - 스프링에 다양히 사용

  • 리플렉션 단점(잘못 사용한 경우)
    - 범용적으로 쓰는 리플렉션을 너무 많이 쓰면 성능 이슈 야기
    - 컴파을 타임에 확인되지 않고 런타임 시에만 발생하는 문제를 만들 가능성 있음
    - 접근 지시자 무시 할 수 있음

  • 리플렉션 사용
    - 스프링 (의존성 주입, MVC 뷰에서 넘어온 데이터를 객체에 바인딩 할 때)
    - 하이버네이트 (@Entity 클래스에 Setter가 없다면 리플렉션 사용)


다이나믹 프록시

스프링 데이터의 JPA 동작

  • 스프링 데이터 JPA에서 인터페이스 타입의 인스턴스는 누가 만들어 주는 것인가?
    - Spring AOP를 기반으로 동작하며 RepositoryFactorySupport에서 프록시를 생성한다.

  • 프록시 서버 : 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램, 서버와 클라이언트 사이에 중계기로서 대리로 통신을 수행하는 것을 “프록시”

  • AOP : 관점 지향 프로그래밍(어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화)


프록시 패턴

  • 프록시와 리얼 서브젝트와 공유하는 인터페이스가 있고, 클라이언트는 해당 인터페이스 타입으로 프록시를 사용
  • 클라이언트는 프록시를 거쳐서 리얼 서브젝트를 사용하기 때문에 프록시는 서브젝트에 대한 접근을 관리하거나 부가기능을 제공하거나, 리턴값을 변경할 수도 있다.
  • 리얼 서브젝트는 자신이 해야 할 일만 하면서 프록시를 사용해서 부가적인 기능 (접근 제한, 로깅, 트랜잭션 등) 을 제공할 때 이런 패턴을 주로 사용

다이나믹 프록시

런타임에 특정 인터페이스를 구현하는 클래스 또는 인스턴스를 만드는 기술

클래스의 프록시가 필요하다면?

서브 클래스를 만들 수 있는 라이브러리를 사용하여 프록시를 만들 수 있다.

  • 서브 클래스를 만드는 방법의 단점
    - 상속을 사용하지 못하는 경우 프록시를 만들 수 없다.
    - Private 생성자만 있는 경우
    - FInal 클래스인 경우
    - 인터페이스가 있을 때는 인터페이스의 프록시를 만들어 사용할 것

다이나믹 프록시 정리

  • 다이나믹 프록시
    - 런타임에 인터페이스 또는 클래스의 프록시 인스턴스 또는 클래스를 만들어 사용하는 프로그래밍 기법
  • 다이나믹 프록시 사용처
    - 스프링 데이터 JPA
    - 스프링 AOP
    - Mockito
    - 하이버네이트 lazy initialization

0개의 댓글