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 like AutowiredAnnotationBeanPostProcessor 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.