참고:
- 이 자습서는 Oracle에서 제공하는 무료 실습 환경에서 사용할 수 있습니다.
- Oracle Cloud Infrastructure 인증서, 테넌시 및 구획에 대한 예제 값이 사용됩니다. 랩을 완료한 후에는 이러한 값을 클라우드 환경과 관련된 값으로 대체하십시오.
Reflection 및 GraalVM Native Image 이해
소개
이 실습은 GraalVM Native Image 내에서 반사가 작동하는 방식에 대해 더 자세히 알고자 하는 개발자를 위한 것입니다.
GraalVM Native Image는 Java 애플리케이션을 독립형 실행 파일로 사전 컴파일할 수 있도록 해줍니다. GraalVM Native Image를 사용하면 런타임에 응용 프로그램에 필요한 코드만 고유 실행 파일에 추가됩니다.
이러한 기본 실행 파일에는 다음과 같은 여러 가지 중요한 이점이 있습니다.
- JVM에서 필요한 리소스의 일부분을 사용하므로 더 저렴함
- 밀리초 단위로 시작
- 제약 없이 신속하게 최상의 성능 제공
- 더 빠르고 효율적인 배포를 위해 경량 컨테이너 이미지에 패키지화할 수 있습니다.
- 공격 표면 감소 (미래의 랩)
오늘날 많은 마이크로서비스 프레임워크에서 Micronaut, Spring, Helidon, Quarkus 등 GraalVM Native Image를 사용한 사전 컴파일을 지원합니다.
또한 네이티브 이미지용 Maven 및 Gradle 플러그인이 있어 Java 애플리케이션을 고유 실행 파일로 쉽게 구축, 테스트 및 실행할 수 있습니다.
주: OCI(Oracle Cloud Infrastructure)는 추가 비용 없이 GraalVM Enterprise를 제공합니다.
예상 랩 시간: 30분
랩 목표
이 연습에서는 다음 작업을 수행합니다.
native-image빌드 툴을 사용하여 독립형 실행 파일로 리플렉션을 사용하는 Java 코드를 작성하는 방법 학습- GraalVM에서 사용할 수 있는 지원 구성 툴에 대해 알아보기
참고: 랩톱 아이콘이 표시될 때마다 수행해야 할 사항이 있습니다. 여기서 확인해 보십시오.

# This is where we you will need to do something
개발 환경은 원격 호스트(Oracle Linux 8, 1 CPU 및 32GB 메모리 포함)에 의해 제공됩니다.
원격 호스트가 준비되기 전에 Luna Labs 데스크탑 환경이 표시되므로 최대 2분이 걸릴 수 있습니다.
STEP 1: 가상 호스트에 연결하고 개발 환경을 확인합니다.
Luna Desktop 환경에서 설치 스크립트를 실행하여 원격 호스트에 연결합니다. 이 스크립트는 리소스 탭을 통해 사용할 수 있습니다.
-
바탕 화면에서 Luna-Lab.html 아이콘을 두 번 누릅니다. 그러면 Oracle Cloud Infrastructure 자격 증명과 실습 관련 정보가 표시됩니다.
-
리소스 탭이 표시됩니다. 컴퓨트 인스턴스가 teh 클라우드에서 프로비저닝되는 동안 리소스 제목 옆에 표시된 안개가 회전합니다.
-
인스턴스가 프로비전되면 최대 2분 정도 걸릴 수 있으며 이때 리소스 탭에 다음과 같은 메시지가 표시됩니다.

-
리소스 탭에서 VS 코드 환경을 설정하는 구성 스크립트를 복사합니다. 세부정보 보기 링크를 눌러 구성을 표시합니다. 아래 스크린샷에 표시된 대로 복사합니다.

-
아래 스크린샷에 표시된 대로 터미널을 엽니다.

-
구성 코드를 터미널에 붙여넣어 VS 코드를 엽니다.


모든 것입니다. 축하합니다. 이제 Oracle Cloud의 원격 호스트에 성공적으로 접속되었습니다!
STEP 2: 마감된 세계 가정
GraalVM과 함께 제공되는 native-image 도구로 독립형 실행 파일은 Java 응용 프로그램 구축과는 약간 다릅니다. 네이티브 이미지는 닫힌 세계 가정이라고 하는 것을 사용합니다.
닫힌 세계는 응용 프로그램에서 런타임 시 호출할 수 있는 모든 바이트 코드를 빌드 시 알고 있어야 합니다(즉, native-image 툴이 독립형 실행 파일을 빌드하는 경우).
GraalVM Native Image를 사용하여 구축된 애플리케이션의 개발/실행 모델을 계속 사용하는 것이 중요합니다.
- Java 소스 코드를 Java 바이트 코드 클래스로 컴파일합니다.
native-image툴을 사용하여 해당 Java 바이트 코드 클래스를 고유 실행 파일로 작성합니다.- 고유 실행 파일을 실행합니다.
하지만, 2단계에서 실제로 어떤 일이 발생합니까?
먼저 native-image 도구가 분석을 수행하여 애플리케이션 내에서 연결할 수 있는 클래스를 확인합니다. 이 내용은 좀더 자세히 살펴보겠습니다.
둘째, 안전하게 초기화될 것으로 알려진(안전 클래스의 자동 초기화) 찾은 클래스가 초기화됩니다. 초기화된 클래스의 클래스 데이터는 이미지 힙에 로드되어 다시 독립형 실행 파일(텍스트 섹션)에 저장됩니다. 이 기능은 GraalVM native-image 도구의 기능 중 하나로, 빠르게 시작되는 애플리케이션을 위한 것입니다.
참고: 객체 초기화와 동일하지 않습니다. 객체 초기화는 고유 실행 파일의 런타임 중에 발생합니다.
접근 가능성 토픽으로 돌아왔다고 말했습니다. 앞에서 언급했듯이 분석에서 독립형 실행 파일에 포함해야 하는 클래스, 메소드 및 필드를 결정합니다. 이 분석은 정적이며 코드를 실행하지 않습니다. 분석에서 동적 클래스 로딩의 일부 케이스를 확인하고 반사( 참조)를 사용할 수 있지만 선택할 수 없는 경우도 있습니다.
Java의 동적 기능을 다룰 수 있으려면 어떤 클래스에서 고찰을 사용하는지 또는 급격하게 로드되는 클래스에 대한 분석이 표시되어야 합니다.
예를 살펴보겠습니다.
STEP 3: 반사 사용 예제
ReflectionExample.java 클래스가 있다고 가정해 보십시오. 이 복사본은 demo/ReflectionExample.java 디렉토리에서 찾을 수 있습니다.
import java.lang.reflect.Method;
class StringReverser {
static String reverse(String input) {
return new StringBuilder(input).reverse().toString();
}
}
class StringCapitalizer {
static String capitalize(String input) {
return input.toUpperCase();
}
}
public class ReflectionExample {
public static void main(String[] args) throws ReflectiveOperationException {
String className = args[0];
String methodName = args[1];
String input = args[2];
Class<?> clazz = Class.forName(className);
Method method = clazz.getDeclaredMethod(methodName, String.class);
Object result = method.invoke(null, input);
System.out.println(result);
}
}
먼저 VS 코드 내에서 터미널을 만듭니다. 이 작업은 Terminal > New Terminal에서 수행합니다.
다음에는 코드를 작성해 보겠습니다. 셸에서 VS 코드 내에서 다음 명령을 실행합니다.

javac ReflectionExample.java
ReflectionExample 클래스의 main 메소드는 이름이 인수로 전달된 클래스를 매우 동적 사용 사례로 로드합니다. 클래스의 두번째 인수는 호출해야 하는 동적으로 로드되는 클래스의 메소드 이름입니다.
이제 그것을 실행하고 그것이 무엇인지 살펴보겠습니다.

java ReflectionExample StringReverser reverse "hello"
예상한 대로 고찰을 통해 StringReverser 클래스의 reverse 메소드를 찾았습니다. 이 메소드는 호출되었으며 "hello" 입력 문자열을 변경했습니다. 지금까지 훌륭합니다.
프로그램 외에 기본 이미지를 구축하려고 하면 어떻게 됩니까? 시도해 보겠습니다. 셸에서 다음 명령을 실행합니다.

native-image --no-fallback ReflectionExample
참고: 독립형 고유 executabale을 작성할 수 없는 경우
native-image에 대한--no-fallback옵션을 사용하면 빌드가 실패합니다.
이제 생성된 고유 실행 파일을 실행하고 다음 작업을 수행해 보겠습니다.

./reflectionexample StringReverser reverse "hello"
Exception in thread "main" java.lang.ClassNotFoundException: StringReverser
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
at java.lang.Class.forName(DynamicHub.java:1214)
at ReflectionExample.main(ReflectionExample.java:21)
무슨 일입니까? 고유의 실행 파일이 클래스 StringReverser를 찾을 수 없는 것 같습니다. 원인이 무엇입니까? 저는 지금 우리가 그 이유를 생각할 것입니다. 닫힌 세계 가정입니다.
native-image 도구가 수행한 분석 중에는 클래스 StringReverser가 사용되었는지 확인할 수 없습니다. 따라서 생성된 고유 실행 파일에서 클래스를 제거했습니다. 주: 독립형 실행 파일에서 원치 않는 클래스를 제거하면 사용할 것으로 알려진 클래스만 포함하여 작성되는 코드를 축소하는 도구가 있습니다. 앞에서 살펴본 바와 같이, 이렇게 하면 반사와 관련된 문제가 발생할 수 있지만 언제든지 이 문제를 해결할 수 있습니다.
STEP 4: 고유 이미지 반사 구성 소개
native-image 빌드 도구에 특수 구성 파일을 통해 반사 인스턴스에 대해 알릴 수 있습니다. 이러한 파일은 JSON로 작성되며 플래그를 사용하여 native-image 도구로 전달될 수 있습니다.
따라서 native-image 빌드 도구에 전달할 수 있는 다른 유형의 구성 정보는 무엇입니까? 이 툴은 다음에 대한 세부 정보가 포함된 파일 읽기를 현재 지원합니다.
- 반사
- 리소스 - 응용 프로그램에 필요한 리소스 파일
- Jni
- 동적 프록시
- 일련번호 지정
우리는 이 랩에서 리플렉션을 처리하는 방법만을 원하므로 해당 분야에 집중할 것입니다.
다음은 이러한 파일이 표시되는 예입니다(여기에서 가져옴).
[
{
"name" : "java.lang.Class",
"queryAllDeclaredConstructors" : true,
"queryAllPublicConstructors" : true,
"queryAllDeclaredMethods" : true,
"queryAllPublicMethods" : true,
"allDeclaredClasses" : true,
"allPublicClasses" : true
},
{
"name" : "java.lang.String",
"fields" : [
{ "name" : "value" },
{ "name" : "hash" }
],
"methods" : [
{ "name" : "<init>", "parameterTypes" : [] },
{ "name" : "<init>", "parameterTypes" : ["char[]"] },
{ "name" : "charAt" },
{ "name" : "format", "parameterTypes" : ["java.lang.String", "java.lang.Object[]"] }
]
},
{
"name" : "java.lang.String$CaseInsensitiveComparator",
"queriedMethods" : [
{ "name" : "compare" }
]
}
]
여기에서는 Reflection API를 통해 액세스되는 클래스와 메소드를 구성해야 함을 알 수 있습니다. 수동으로 이 작업을 수행할 수 있지만 이러한 구성 파일을 생성하는 가장 편리한 방법은 지원되는 구성 javaagent을 사용하는 것입니다.
STEP 5: 고유 이미지, 보조 구성: Java 에이전트를 입력합니다.
전체 반사 구성 파일을 처음부터 새로 작성할 수는 있지만 GraalVM Java 런타임은 응용 프로그램을 실행할 때 자동으로 이 파일을 생성하는 java 추적 에이전트 javaagent를 제공합니다.
시도해 보겠습니다.
추적 에이전트가 활성화된 상태에서 응용 프로그램을 실행합니다. 셸에서 다음을 실행합니다.

# Note: the tracing agent parameter must come before the classpath and jar params on the command ine
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"

생성된 구성을 살펴보겠습니다.

cat META-INF/native-image/reflect-config.json
[
{
"name":"StringReverser",
"methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
}
]
아래 예제와 같이 native-image-agent=config-merge-dir를 지정하면 이 프로세스를 여러 번 실행할 수 있으며 실행이 병합됩니다.

java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
이제 독립형 실행 파일을 생성하면 제공된 구성이 사용됩니다. 구축 방법:

native-image --no-fallback ReflectionExample
그럼 이제 좀 더 잘 작동하는지 살펴보겠습니다.

./reflectionexample StringReverser reverse "hello"
정답입니다!
결론
GraalVM Native Image로 독립형 실행 파일을 만드는 작업은 닫힌 세계 가정에 의존하므로 코드에서 발생할 수 있는 반사 사례에 대해 독립형 실행 파일을 작성할 때 미리 알아야 합니다.
GraalVM 플랫폼은 반사가 사용될 때 native-image 빌드 도구에 지정할 수 있는 방법을 제공합니다. 주: 몇 가지 간단한 경우 native-image 도구에서 이러한 내용을 자체적으로 검색할 수 있습니다.
GraalVM 플랫폼은 Java 추적 에이전트를 통해 반사 및 기타 동적 동작의 사용을 검색하는 방법도 제공하며 native-image 도구에 필요한 구성 파일을 자동으로 생성할 수 있습니다.
추적 에이전트를 사용할 때는 몇 가지 사항을 염두에 두어야 합니다.
- 테스트 모음을 사용합니다. 코드에서 가능한 많은 경로를 연습해야 합니다.
- 구성 파일을 검토하고 편집해야 할 수 있습니다.
이 자습서를 즐기시고 네이티브 이미지를 사용할 때 반사 작업을 수행할 수 있는 방법에 대해 알아보겠습니다.
더 알아보기
- Native Image Architect Christian Wimmer GraalVM Native Image: Large Static Analysis for Java의 프레젠테이션 보기
- GraalVM EE Native Image Reference 설명서
- 네이티브 이미지에서 반사 사용
- 기본 이미지에서 클래스 초기화
- 추적 에이전트를 사용한 지원 구성
추가 학습 자원
docs.oracle.com/learn에서 다른 실습을 찾아보거나 Oracle Learning YouTube channel에서 무료 학습 콘텐츠에 액세스할 수 있습니다. 또한 education.oracle.com/learning-explorer를 방문하여 Oracle Learning Explorer로 변경하십시오.
제품 설명서는 Oracle Help Center를 참조하십시오.
Understanding Reflection and GraalVM Native Image
F54903-01
March 2022
Copyright © 2022, Oracle and/or its affiliates.