Affects: Spring 6.0.2 / SpringBoot 3.0.0 / GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08)
Injection of spring beans fails when using @Resource
and native image.
When using @Autowired
the dependency injections works as expected.
example project: https://github.com/javamichi18/graalDependencyTest
e.g.
@Controller
public class API {
@jakarta.annotation.Resource //--> NPE
// @Autowired // --> works
private TestService service;
mvn spring-boot:build-image -Pnative2
...
[INFO] Successfully built image 'docker.io/library/gdt:0.0.1-SNAPSHOT'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:25 min
[INFO] Finished at: 2022-11-30T14:30:50+01:00
Run app: graalDependencyTest % ./target/gdt
Invocation of REST endpoint ...
curl http://localhost:8080/foo
{"timestamp":"2022-11-30T13:34:36.062+00:00","status":500,"error":"Internal Server Error","path":"/foo"}%
... triggers NullPointerException when using @Resource
, but not when using@Autowired.
2022-11-30T14:32:52.710+01:00 ERROR 28966 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at com.example.graaldependencytest.API.getFoo(API.java:32) ~[gdt:na]
at java.base@17.0.5/java.lang.reflect.Method.invoke(Method.java:568) ~[gdt:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207) ~[gdt:6.0.2]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152) ~[gdt:6.0.2]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[gdt:6.0.2]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) ~[gdt:6.0.2]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[gdt:6.0.2]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[gdt:6.0.2]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080) ~[gdt:6.0.2]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973) ~[gdt:6.0.2]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1003) ~[gdt:6.0.2]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:895) ~[gdt:6.0.2]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:705) ~[gdt:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880) ~[gdt:6.0.2]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814) ~[gdt:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[gdt:10.1.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[gdt:6.0.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[gdt:6.0.2]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[gdt:6.0.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[gdt:6.0.2]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[gdt:6.0.2]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[gdt:6.0.2]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185) ~[na:na]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158) ~[na:na]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[na:na]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[na:na]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[gdt:10.1.1]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119) ~[na:na]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[gdt:10.1.1]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[na:na]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[na:na]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400) ~[na:na]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[gdt:10.1.1]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) ~[na:na]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739) ~[na:na]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[gdt:10.1.1]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[na:na]
at java.base@17.0.5/java.lang.Thread.run(Thread.java:833) ~[gdt:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:775) ~[gdt:na]
at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:203) ~[na:na]
environment:
java -version openjdk version "17.0.5" 2022-10-18 OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08) OpenJDK 64-Bit Server VM GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08, mixed mode, sharing)
Apple Silicon M1 max
I am using the maven build profile native2, because using
mvn spring-boot:build-image -Pnative
leads to a stuck build / running endlessness (> 45 minutes).
Comment From: hantsy
I think Spring should remove jakarta.annotation.Resource
support(like removing EJB support).
Jakarta EE itself is cleaning up the dependency injection support in all specs, and trying to move all injection provider to CDI. Such as erasing the injection provider in JSF, REST etc.
The jakarta.annotation.Resource
(mainly for JNDI resource inject), JNDI Lookup, EJB are almost deprecated, I think they will not be updated in future.
Comment From: sdeleuze
Confirmed, the required reflection hints are missing when using @Resource
.
Comment From: sdeleuze
CommonAnnotationBeanPostProcessor
should probably be made AOT aware like AutowiredAnnotationBeanPostProcessor
is.
Comment From: sdeleuze
This is doable but more involved than expected, so I prefer to focus on the other issues for 6.0.5
.
Comment From: sdeleuze
As we try to empty 6.0.x
bucket, and given the various milestone date and summer PTO season, I am moving it back to 6.1.x
. Does not prevent to fix it in an upcoming 6.0 specific release, but let's decide when we have more visibility.
Comment From: sbrannen
CommonAnnotationBeanPostProcessor
should probably be made AOT aware likeAutowiredAnnotationBeanPostProcessor
is.
That should cover support for the application.
However, I think we will need something like AutowiredAnnotationBeanPostProcessor
's processInjection(Object)
in CommonAnnotationBeanPostProcessor
in order to support the use of @Resource
in test classes in AOT mode (see also DependencyInjectionTestExecutionListener.injectDependenciesInAotMode(...)
).
@jhoeller, thoughts?
Comment From: sbrannen
While running tests such as RollbackForRequiresNewEjbTxDaoTestNGTests
in the spring-test
module in AOT mode, I noticed that we also do not support dependency injection via @EJB
(jakarta.ejb.EJB
).
I've updated the title of this issue to reflect that.
Comment From: snicoll
Let's keep the scope of the issue as it is now please. While technically those two are related, I don't see a reason to spend efforts to suport @EJB
for AOT at this time.