java - spring-boot 3.0.0 GraaVM 用于接口(interface) jakarta.servlet.http.HttpServletRequest

标签 java spring-boot tomcat graalvm jakarta-migration

刚刚使用 GraalVM Native 测试了一些 Spring Boot 3.0.0,并收到了一些关于它的疑问,因为我找不到相关的正确文档。

因此,我在 Spring Initializr ( https://start.spring.io/ ) 上启动了一个包含 GraalVM native 支持和 Spring Web 依赖项的新项目。

然后,为了测试 native 图像,我有如下所示的 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 <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="711b1007105f131002143140465f415f44" rel="noreferrer noopener nofollow">[email protected]</a>/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[com.example.demo.DemoApplication:na]
        at <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="452f2433246b272436200574726b756b70" rel="noreferrer noopener nofollow">[email protected]</a>/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

我认为这一定与通知接口(interface)jakarta.servlet.http.HttpServletRequest的实现有关,但是我不知道如何通知/配置它。

大家有什么建议吗?

提前致谢。

最佳答案

在阅读了 Spring Boot 3.0.0 ( https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/ ) 用户指南后,发现 Spring AOT 的某些类需要运行时提示才能将其编译为 native 程序。 如果未找到该类,您需要创建自定义提示 ( https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#native-image.advanced.custom-hints )。

为了让它工作,我创建了一个 HTTPServletRequest 运行时提示(记住必须实现接口(interface) 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);
    }

}

完成!

希望有帮助!

谢谢。

关于java - spring-boot 3.0.0 GraaVM 用于接口(interface) jakarta.servlet.http.HttpServletRequest,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74829933/

相关文章:

java - 使用 spring-mvc 在网页上显示验证错误?

java - 用于创建 JasperReport 后未释放池连接

java - 如何知道 getValueAt() 返回的单元格的类型?

java - Android studio “unfortunately [project name] stopped working”,有关如何修复的任何线索

java - 通过 POST 从 React App 发送数组到 Spring Boot

java - 使用 Java 标准 keystore 是一种不好的做法吗

Spring Cloud Stream Kafka Streams Binder KafkaException : Could not start stream: 'listener' cannot be null

oracle - 列出所有数据库表 - JPA

java - 无法在没有 Moskito UI 的情况下找到 Moskito 与 java web 应用程序集成的解决方案

java - 使用 Tomcat 部署现有的 webapp