Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring: @Resource injection stopped working under JDK9

In my @Configuration class I have dependendies like the following:

@Configuration
public class MyConfig {
    @Resource(name = "firstDataSource")
    private DataSource firstDataSource;

    // more code
}

The dependency injection worked in Oracle JDK 8: firstDataSource field was successfully injected with a non-null value.

Now I tried to run the application (with no modifications) in JDK 9. The result is that @Resource does not trigger dependency injection anymore: everything annotated with this annotation remains null.

What could be the reason for @Resource to stop working?

Spring 4.0.9 is used in the project.

Here is a test project demonstrating the problem: https://github.com/rpuch/test-spring-injection-jdk9

It contains a single test: MainTest which I run from my IDE. When I use JDK 8, it outputs

сен 29, 2017 10:45:13 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5f8ed237: startup date [Fri Sep 29 22:45:13 SAMT 2017]; root of context hierarchy
OK

But under JDK 9, it throws an exception during startup which is caused by the following:

Caused by: java.lang.IllegalStateException: bean1 is not injected
    at Bean2.<init>(Bean2.java:7)
    at Config2.bean2(Config2.java:16)

which happens when the dependency is not injected.

like image 362
Roman Puchkovskiy Avatar asked Sep 29 '17 17:09

Roman Puchkovskiy


2 Answers

The java.xml.ws.annotation being deprecated in jdk9 is possibly the cause of what you're facing. Since the annotation @Resource is from the package javax.annotation exported by the same module.

You might want to provide javatm-common-annotations as an upgradeable module for java.xml.ws.annotation which exports the javax.annotation.

This standalone release of Java(TM) Common Annotations uses a Java Platform Module System "automatic" module name of java.annotation, to match the module name used in JDK 9.

The search on central suggests, you can use:-

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>

This runs fine on my machine with your shared code.


Also, a note about the code that you've shared. Since you're migrating to using JDK9, you should migrate to using latest springframework dependencies released as on 28-9-2017:-

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.0.RELEASE</version>
</dependency>

Edit : Do take a look at Roman's answer for an alternate to make use of --add-modules in case you are not planning to migrate to the javaee explosed module yet.

like image 65
Naman Avatar answered Nov 15 '22 17:11

Naman


Adding a few missing details.

Spring @Resource-driven injection only works when javax.annotation.Resource is available at run time. Spring makes a check:

private static final boolean jsr250Present =
        ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader());

and then uses that jsr250Present variable to see whether @Resource-based injection (and also @PostConstruct/@PreDestroy functionality) should be enabled.

In my case, under JDK 9, this class was not available in run time because it belongs to a separate module java.ws.xml.annotation (separate from the basic java.base module containing java.lang and some other packages that is always available). To solve the problem, one of the following approaches may be taken:

  1. Add javax.annotation-api library to my application classpath, as @AlanBateman and @nullpointer suggested.
  2. As an alternative, java may be instructed to add the module using a command line switch: --add-modules java.xml.ws.annotation. This leaves the code intact.
like image 43
Roman Puchkovskiy Avatar answered Nov 15 '22 17:11

Roman Puchkovskiy