You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Spring Boot中用GraalVM执行Python脚本遇空指针问题求助

Spring Boot + GraalVM执行Python脚本无法获取类成员的调试方案

问题背景

在Spring Boot 2.6.13 + GraalVM 22.3.0环境下,执行Python脚本时无法获取StacCreator类,pystacCreatorClass为空。但相同代码在非Spring Boot的简单Java库中可正常运行,且GraalVM Context能执行简单Python代码(比如print("Hello World"))。

Java代码(Spring Boot Controller)

@GetMapping("/foo")
public String foo() {
    
    String PYTHON = "python";
    String VENV_EXECUTABLE = MainController.class.getClassLoader().getResource(Paths.get("venv", "bin", "graalpy").toString()).getPath();
    String SOURCE_FILE_NAME = "staccreator.py";
            
    Context context = Context.newBuilder(PYTHON).
            allowAllAccess(true).
            option("python.Executable", VENV_EXECUTABLE).
            option("python.ForceImportSite", "true").
            build();
    
    context.eval("python", "print(\"Hello World\");");
    
    InputStreamReader code = new InputStreamReader(MainController.class.getClassLoader().getResourceAsStream(SOURCE_FILE_NAME));               
    Source source;
    try {
        source = Source.newBuilder(PYTHON, code, SOURCE_FILE_NAME).build();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    
    System.out.println(source);
    
    context.eval(source);
          
    Set<String> keys = context.getPolyglotBindings().getMemberKeys();
    System.out.println(keys.size());

    Value pystacCreatorClass = context.getPolyglotBindings().getMember("StacCreator");
    Value pystacCreator = pystacCreatorClass.newInstance();
    
    StacCreator stacCreator = pystacCreator.as(StacCreator.class);
    // stacCreator do something

    context.close(); 
    return "foo";
}

Python脚本(staccreator.py)

import polyglot
import os

class StacCreator:
    def create(self, collection_file_path):
        print("Hello from Python.")

错误信息

2022-10-31 08:58:01.139 ERROR 9668 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException: Cannot invoke "org.graalvm.polyglot.Value.newInstance(Object[])" because "pystacCreatorClass" is null] with root cause

java.lang.NullPointerException: Cannot invoke "org.graalvm.polyglot.Value.newInstance(Object[])" because "pystacCreatorClass" is null
    at com.example.staccreator.MainController.foo(MainController.java:61) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:670) ~[tomcat-embed-core-9.0.68.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:779) ~[tomcat-embed-core-9.0.68.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.68.jar:9.0.68]
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

调试与解决步骤

1. 确认Python脚本是否正确加载并执行

在Python脚本开头添加打印语句,验证脚本是否被GraalVM正确执行:

print("StacCreator script loaded")
import polyglot
import os

class StacCreator:
    def create(self, collection_file_path):
        print("Hello from Python.")

如果控制台没有输出该语句,说明脚本未被正确加载,需要检查:

  • SOURCE_FILE_NAME对应的资源路径是否正确,Spring Boot打包后资源文件是否在classpath中
  • 使用MainController.class.getClassLoader().getResource(SOURCE_FILE_NAME)检查返回值是否为null,确认资源存在

2. 显式将类暴露到Polyglot绑定

默认情况下,GraalVM Python不会自动将脚本中的类暴露到Polyglot全局绑定。修改Python脚本,显式将StacCreator类添加到全局绑定:

import polyglot
import os

class StacCreator:
    def create(self, collection_file_path):
        print("Hello from Python.")

# 显式暴露类到全局Polyglot绑定
polyglot.export_value("StacCreator", StacCreator)

3. 检查Python执行环境的差异

Spring Boot环境下的类加载器和普通Java程序不同,可能导致GraalVM Python的执行路径或site包加载异常:

  • 打印VENV_EXECUTABLE的实际路径,确认虚拟环境路径正确,且文件存在
  • 移除python.ForceImportSite配置,或检查虚拟环境的site-packages是否能被正确加载
  • 尝试不使用虚拟环境,直接使用GraalVM自带的Python执行,排除虚拟环境的影响

4. 调试Polyglot绑定的所有成员

在获取StacCreator之前,打印所有绑定的成员,确认是否有该类:

Set<String> keys = context.getPolyglotBindings().getMemberKeys();
System.out.println("All polyglot bindings:");
for (String key : keys) {
    System.out.println("- " + key);
}

如果输出中没有StacCreator,说明类未被正确暴露;如果有但名称不一致(比如大小写问题),需要修正获取的名称。

5. 检查Spring Boot的类加载器隔离

Spring Boot使用自定义类加载器,可能导致GraalVM无法正确读取资源或加载Python环境:

  • 尝试将Python脚本和虚拟环境路径改为绝对路径,避免类加载器的影响
  • 尝试在非Controller的普通Bean中执行相同代码,确认是否是Controller环境的问题

6. 版本兼容性检查

确认Spring Boot 2.6.13和GraalVM 22.3.0的兼容性:

  • GraalVM 22.3.0对应Java 17,检查Spring Boot是否运行在Java 17环境下
  • 尝试升级Spring Boot到2.7.x版本(兼容Java 17),或降低GraalVM版本到22.1.x,排除版本不兼容问题

内容的提问来源于stack exchange,提问作者Stefan Ziegler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 00:02:25