spring-boot 3.0.0 GraaVM для интерфейса jakarta.servlet.http.HttpServletRequest
Просто протестировал Spring Boot 3.0.0 с GraalVM Native и получил несколько вопросов по этому поводу, так как не смог найти надлежащую документацию по этому поводу.
Итак, я начал новый проект с собственной поддержкой GraalVM и зависимостями Spring Web на Spring Initializr (https://start.spring.io/).
Затем для тестирования собственного изображения у меня есть класс DemoApplication, как показано ниже:
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);
}
}
В результате для сборки нативного образа использовалась следующая команда:
mvn -Pnative spring-boot:build-image
Образ был успешно скомпилирован и создан:
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
Когда я запускаю этот «демонстрационный» образ, используя:
docker run demo:0.0.1-SNAPSHOT
Он показывает следующее исключение:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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 java.base@17.0.5/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[com.example.demo.DemoApplication:na]
at java.base@17.0.5/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
Я предполагаю, что это должно быть что-то связанное с информацией о реализации интерфейсаjakarta.servlet.http.HttpServletRequest
, однако я не знаю, как это сообщить/настроить.
Ребята, у вас есть предложения?
Заранее спасибо.
1 ответ
После нескольких чтений руководства пользователя Spring Boot 3.0.0 (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) выяснилось, что некоторым классам Spring AOT требуется подсказка во время выполнения в качестве подсказки. результат, чтобы скомпилировать его в собственную программу. Если класс не найден, вам нужно создать собственную подсказку (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#native-image.advanced.custom-hints).
Итак, чтобы это сработало, я создал подсказку времени выполнения HTTPServletRequest (имейте в виду, что должен реализовывать интерфейсorg.springframework.aot.hint.RuntimeHintsRegistrar
) класса следующим образом:
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());
}
}
}
И тогда его нужно информировать о@Configuration
класс или, например, ваш@SpringBootApplication
аннотированный класс приложения для активации этих подсказок:
@SpringBootApplication
@ImportRuntimeHints(value = {HttpServletRequestRuntimeHint.class})
public class DemoApplication {
@Autowired
private HttpServletRequest request;
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
}
}
Готово!
Надеюсь, это поможет!
Спасибо.