Giriş
With a clean or very small Spring Boot application it might work out of the box. However for most applications it will not work in this way because of a GraalVM reflection incompatibility.
In this case, there will be error notifications such as the following:
Warning: Could not resolve org.h2.Driver for reflection configuration. Reason: java.lang.ClassNotFoundException: org.h2.Driver.
Bir başka açıklama
şöyle. Yani GraalVM sadece statik analiz ile sınıfları bulmaya çalışıyor, ama Java'da çok fazla reflection kullanıldığı için GraalVM'in tüm kodları bulma şansı yok. Bu yüzden
java.lang.NoClassDefFoundError: Could not initialize class ... veya ClassNotFoundException gibi hatalar alıyoruz
When you use Native Image to build native executables it only includes the elements reachable from your application entry point, its dependent libraries, and JDK classes discovered through static analysis. However, the reachability of some elements (such as classes, methods, or fields) may not be discoverable due to Java’s dynamic features including reflection, resource access, dynamic proxies, and serialization. If an element is not reachable, it is not included in the generated executable at build time, which can lead to failures at run time. Native Image has built-in metadata for JDK classes but user code and dependencies may use dynamic features of Java that are undiscoverable by the Native Image analysis. For this reason, Native Image accepts additional reachability metadata in the form of JSON files. Since this metadata is specific to a specific code base, the JSON files providing the corresponding metadata can be shared for libraries and frameworks. This repository is a centralized place for sharing such files for libraries and frameworks that do not provide built-in metadata yet. It is also used to retrofit metadata for older versions of libraries and frameworks.
Çıktı JSON Dosyaları
reflection kullanan sınıfları reflection-config.json dosyasına yazmak gerekir. Dosyanın yolu şöyle
/src/main/resources/META-INF/native-image/reflect-config.json
Dosyayı Üretmek
1. java ... komutu ile uygulama çalıştırılır
2. Kodun çeşitli yerlerinin koşması sağlanır. Örneğin uygulamaya REST istekleri gönderiririz
3. Ctrl + C ile agent sonlandırılır. Agent çıktıyı dosyalara yazar
config-output-dir seçeneği
Normalde dosyaların src/main/resources/META-INF/native-image/ dizininde olması gerekir ama bir sebepten farklı bir hedef dizin vermek gerekirse bu seçenek kullanılır.
Örnek
java -agentlib:native-image-agent=config-output-dir=target/ \
-jar native-0.0.1-SNAPSHOT.jar
target/target dizini altında şu dosyalar üretilir
proxy-config.json
reflect-config.json
resource-config.json
config-merge-dir seçeneği
Örnek
Şöyle
yaparız. config-merge-dir seçeneği ile mevcut dosyaya ekleme yapılır
java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image \
-jar target/my-application-1.0.0-SNAPSHOT.jar
This command will generate in the folder the following files:
jni-config.json
predefined-classes-config.json
proxy-config.json
reflect-config.json
resource-config.json
serialization-config.json
With the generated metadata and the fixed initialization time of the classes, the native image build should be successful. Nonetheless, at runtime there could come up more errors. The most common one is the ClassNotFoundException. That means that the configuration in the reflect-config.json is incomplete and you should add the class. Another similar error is a FileNotFoundException because a file could not be located in the classpath. This means that the required file is missing in the resource-config.json.
Eğer META-INF/native-image yerine başka bir dizin kullanmak istersek şöyle
yaparız<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<buildArgs>
<buildArg>-H:ConfigurationResourceRoots=path/to/resources/</buildArg>
</buildArgs>
</configuration>
Dosyanın İçin Nasıldır
Örnek
[
{
"name":"org.h2.Driver"
}
]
Örnek
[
{
"name": "kotlin.reflect.jvm.internal.ReflectionFactoryImpl",
"allDeclaredConstructors":true
},
{
"name": "kotlin.KotlinVersion",
"allPublicMethods": true,
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true
},
{
"name": "kotlin.KotlinVersion[]"
},
{
"name": "kotlin.KotlinVersion$Companion"
},
{
"name": "kotlin.KotlinVersion$Companion[]"
},
{
"name": "kotlin.internal.jdk8.JDK8PlatformImplementations",
"allPublicMethods": true,
"allDeclaredFields":true,
"allDeclaredMethods":true,
"allDeclaredConstructors":true
}
]