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);
    }

}

Готово!

Надеюсь, это поможет!

Спасибо.

Другие вопросы по тегам