10

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.

Roman Puchkovskiy
  • 11,415
  • 5
  • 36
  • 72
  • What package does the `@Resource` belong to? Did [your code compiled successfully](https://stackoverflow.com/questions/46352120/incompatible-types-equality-constraints-and-method-not-found-during-java-9-migr)? Is there a reproducible instance to test this? – Naman Sep 29 '17 at 18:03
  • It is `javax.annotation.Resource`. Yes, everything compiles correctly. I will try to create a test project to reproduce this. – Roman Puchkovskiy Sep 29 '17 at 18:07
  • And how do you import/add that package in your project module? – Naman Sep 29 '17 at 18:11
  • If by 'module' you mean java module (in the sense of Jigsaw), then I don't have module-info.java as we can't yet depend on Java 9 specific features. Should I import it specifically? – Roman Puchkovskiy Sep 29 '17 at 18:17
  • Yes, I meant [tag:java-module] only. And in that case, you might to share some error/debug log as well with the reproducible instance. – Naman Sep 29 '17 at 18:20
  • Alas, I don't see anything relevant in my log before I get a `NullPointerException` because a non-injected field is accessed during startup. – Roman Puchkovskiy Sep 29 '17 at 18:23
  • In case you are using framework like maven/gradle, could you add to the question, your project's existing *dependency tree*. – Naman Sep 29 '17 at 18:27
  • I've added a test project at github, question updated. – Roman Puchkovskiy Sep 29 '17 at 18:47
  • 1
    Which version of Spring is this? Spring 5 seems to be the first version to support JDK 9. – Alan Bateman Sep 29 '17 at 18:53
  • @AlanBateman Spring version is 4.0.9. I also tried 4.3.11 and 5.0.0, they behave in the same way. – Roman Puchkovskiy Sep 29 '17 at 18:57
  • Does this fix it: ` javax.annotation javax.annotation-api 1.3.1 `? If so then I think we can explain what you are seeing. – Alan Bateman Sep 29 '17 at 19:07
  • Indeed, this fixes the problem, thanks! But how come? I mean, `@Resource` is alreasy at the JDK classpath even without this dependency. – Roman Puchkovskiy Sep 29 '17 at 19:12
  • 1
    @RomanPuchkovskiy The javax.annotation being deprecated in jdk9 is possibly the cause of what you're facing. – Naman Sep 29 '17 at 19:14
  • 1
    Someone needs to write a complete answer but in summary: Java SE defines a small subset of the JSR-250 annotations, just enough to support web services. The module in the JDK with these annotations is not resolved by default. It is "as if" these annotations don't exist. This is the first step to dropping this module from Java SE and the JDK. The reason it builds for you is because your POM is configured to compile with -source/-target 8. If your POM didn't have these properties then I assume it wouldn't compile either. – Alan Bateman Sep 29 '17 at 19:18
  • 1
    @AlanBateman True, if the source and target 9 are used instead of 1.8, it wouldn't compile asking for a module for the annotation at the very first step. – Naman Sep 29 '17 at 19:24

2 Answers2

10

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.

Naman
  • 27,789
  • 26
  • 218
  • 353
10

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.
Roman Puchkovskiy
  • 11,415
  • 5
  • 36
  • 72