// Build-time AOT generation
public class MyAotProcessor extends ContextAotProcessor {
public MyAotProcessor(Class<?> mainClass) {
super(mainClass, Settings.builder()
.sourceOutput(Paths.get("target/spring-aot/main/sources"))
.resourceOutput(Paths.get("target/spring-aot/main/resources"))
.classOutput(Paths.get("target/spring-aot/main/classes"))
.groupId("com.example")
.artifactId("my-app")
.build());
}
@Override
protected GenericApplicationContext prepareApplicationContext(Class<?> appClass) {
GenericApplicationContext context = new GenericApplicationContext();
new AnnotatedBeanDefinitionReader(context).register(appClass);
return context;
}
}
// Execute AOT processing
MyAotProcessor processor = new MyAotProcessor(MyApplication.class);
ClassName initializer = processor.process();public class MyApplication {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
// Load AOT-generated initializer
AotApplicationContextInitializer<GenericApplicationContext> initializer =
AotApplicationContextInitializer.forInitializerClasses(
"com.example.__ApplicationContextInitializer"
);
// Initialize with AOT artifacts
initializer.initialize(context);
context.refresh();
// Run application
}
}@Configuration
@ImportRuntimeHints(MyRuntimeHints.class)
public class AppConfig {}
public class MyRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// Reflection hints
hints.reflection().registerType(
MyClass.class,
MemberCategory.INVOKE_PUBLIC_METHODS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS
);
// Resource hints
hints.resources().registerPattern("config/*.properties");
hints.resources().registerPattern("templates/*.html");
// Serialization hints
hints.serialization().registerType(MySerializableClass.class);
// JNI hints
hints.jni().registerType(NativeLibrary.class);
// Proxy hints
hints.proxies().registerJdkProxy(MyInterface.class);
}
}// Mark for reflective access
@Reflective
@Component
public class MyReflectiveComponent {
@Reflective
public void invokedReflectively() {}
}
// Enable package scanning
@Configuration
@ReflectiveScan(basePackages = "com.example.reflective")
public class ReflectiveConfig {}@Component
public class CustomAotProcessor implements BeanFactoryInitializationAotProcessor {
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
List<String> customBeans = findCustomBeans(beanFactory);
if (customBeans.isEmpty()) {
return null;
}
return (generationContext, beanFactoryInitializationCode) -> {
// Generate initialization code
GeneratedMethod method = beanFactoryInitializationCode
.getMethods()
.add("initCustomBeans", builder -> {
builder.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
builder.addParameter(DefaultListableBeanFactory.class, "beanFactory");
for (String beanName : customBeans) {
builder.addStatement("// Initialize: $L", beanName);
}
});
beanFactoryInitializationCode.addInitializer(method.toMethodReference());
// Register runtime hints
customBeans.forEach(bean -> {
generationContext.getRuntimeHints()
.reflection()
.registerType(TypeReference.of("com.example." + bean),
hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
});
};
}
private List<String> findCustomBeans(ConfigurableListableBeanFactory beanFactory) {
return Arrays.stream(beanFactory.getBeanDefinitionNames())
.filter(this::isCustomBean)
.collect(Collectors.toList());
}
}# META-INF/spring/aot.factories
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
com.example.CustomAotProcessor@Component
public class CustomBeanRegistrationProcessor implements BeanRegistrationAotProcessor {
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
if (!isCustomBean(registeredBean)) {
return null;
}
return (generationContext, beanRegistrationCode) -> {
// Generate bean instantiation code
GeneratedMethod factory = beanRegistrationCode
.getMethods()
.add("create", builder -> {
builder.addModifiers(Modifier.PRIVATE, Modifier.STATIC);
builder.returns(registeredBean.getBeanClass());
builder.addStatement("return new $T()", registeredBean.getBeanClass());
});
beanRegistrationCode.addInstancePostProcessor(factory.toMethodReference());
// Register reflection hints
generationContext.getRuntimeHints()
.reflection()
.registerType(registeredBean.getBeanClass(),
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
};
}
}// Use constructor injection (no reflection needed)
@Component
public class MyService {
private final MyRepository repository;
public MyService(MyRepository repository) { // Detected by AOT
this.repository = repository;
}
}
// Avoid field injection (requires reflection hints)
// @Autowired private MyRepository repository; // Requires hint
// Provide explicit hints for dynamic behavior
@Configuration
@ImportRuntimeHints(DataAccessHints.class)
public class DataConfig {
public static class DataAccessHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(DynamicEntity.class,
MemberCategory.INVOKE_PUBLIC_METHODS);
hints.resources().registerPattern("sql/*.sql");
}
}
}@SpringBootTest
@EnabledInNativeImage // Only run in native image
class NativeImageTests {
@Autowired
private ApplicationContext context;
@Test
void contextLoads() {
assertThat(context).isNotNull();
}
@Test
void beansAreRegistered() {
MyService service = context.getBean(MyService.class);
assertThat(service).isNotNull();
}
}# Enable verbose GraalVM output
native-image \
--verbose \
-H:+PrintClassInitialization \
-H:+PrintFeatures \
-jar my-app.jar
# Add native-image.properties in META-INF/native-image/
Args = --initialize-at-build-time=com.example.config
Args = --initialize-at-run-time=com.example.runtime