Just testing some Spring Boot 3.0.0 with GraalVM Native and got some queries regarding it as I could not find properly documentation regarding it.
So, I've started a new project with GraalVM Native Support and Spring Web depedencies on Spring Initializr (https://start.spring.io/).
Then, for testing native image purposes I have my DemoApplication class like as follows:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import jakarta.servlet.http.HttpServletRequest;
@SpringBootApplication(proxyBeanMethods = false)
public class DemoApplication {
@Autowired
private HttpServletRequest request;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
As a result to build a native image, have used command as follows:
mvn -Pnative spring-boot:build-image
The image was successfully compiled and created:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
paketobuildpacks/run tiny-cnb c71fb787280a 3 days ago 17.3MB
paketobuildpacks/builder tiny cf7ea4946a20 42 years ago 588MB
demo 0.0.1-SNAPSHOT 7794949d07ce 42 years ago 96.9MB
When I run this "demo" image using:
docker run demo:0.0.1-SNAPSHOT
It shows the following exception:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.0)
2022-12-16T21:23:41.386Z INFO 1 --- [ main] com.example.demo.DemoApplication : Starting AOT-processed DemoApplication using Java 17.0.5 with PID 1 (/workspace/com.example.demo.DemoApplication started by cnb in /workspace)
2022-12-16T21:23:41.386Z INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2022-12-16T21:23:41.395Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-12-16T21:23:41.396Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-12-16T21:23:41.396Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.1]
2022-12-16T21:23:41.399Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-12-16T21:23:41.400Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 14 ms
2022-12-16T21:23:41.403Z WARN 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoApplication': Instantiation of supplied bean failed
2022-12-16T21:23:41.403Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2022-12-16T21:23:41.404Z ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoApplication': Instantiation of supplied bean failed
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1236) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[com.example.demo.DemoApplication:3.0.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[com.example.demo.DemoApplication:3.0.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[com.example.demo.DemoApplication:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[com.example.demo.DemoApplication:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[com.example.demo.DemoApplication:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[com.example.demo.DemoApplication:3.0.0]
at com.example.demo.DemoApplication.main(DemoApplication.java:16) ~[com.example.demo.DemoApplication:na]
Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface jakarta.servlet.http.HttpServletRequest] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89) ~[na:na]
at com.oracle.svm.core.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:171) ~[na:na]
at [email protected]/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[com.example.demo.DemoApplication:na]
at [email protected]/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1037) ~[com.example.demo.DemoApplication:na]
at org.springframework.beans.factory.support.AutowireUtils.resolveAutowiringValue(AutowireUtils.java:134) ~[na:na]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1576) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1368) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) ~[com.example.demo.DemoApplication:6.0.2]
at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveValue(AutowiredFieldValueResolver.java:189) ~[na:na]
at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveAndSet(AutowiredFieldValueResolver.java:167) ~[na:na]
at com.example.demo.DemoApplication__Autowiring.apply(DemoApplication__Autowiring.java:14) ~[na:na]
at org.springframework.beans.factory.support.InstanceSupplier$1.get(InstanceSupplier.java:82) ~[na:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ~[com.example.demo.DemoApplication:6.0.2]
... 18 common frames omitted
I assume that must be something related to inform a implementation for interface jakarta.servlet.http.HttpServletRequest
, however I don't how to inform/configure it.
Do you guys have any suggestion(s)?
Thanks in advance.
CodePudding user response:
After several readings on Spring Boot 3.0.0 (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) user guide, have figured out that some classes Spring AOT needs a runtime hint as a result to compile it into a native program. If the class is not found you need to create a custom hint (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#native-image.advanced.custom-hints).
So to have it worked I have created a HTTPServletRequest runtime hint (bear in mind that must implements interface org.springframework.aot.hint.RuntimeHintsRegistrar
) class as follows:
public class HttpServletRequestRuntimeHint implements RuntimeHintsRegistrar{
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
try {
ProxyHints proxies = hints.proxies();
proxies.registerJdkProxy(HttpServletRequest.class);
} catch (Exception e) {
throw new RuntimeException("Could not register RuntimeHint: " e.getMessage());
}
}
}
And then it needs to be informed on a @Configuration
class or, for example, your @SpringBootApplication
annotated application class to activate those hints:
@SpringBootApplication
@ImportRuntimeHints(value = {HttpServletRequestRuntimeHint.class})
public class DemoApplication {
@Autowired
private HttpServletRequest request;
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
And done!
Hope that helps!
Thank you.