62

So, I have a simple properties file with the following entries:

my.value=123
another.value=hello world

This properties file is being loaded using a PropertyPlaceHolderConfigurer, which references the properties file above.

I have the following class, for which I'm trying to load these properties in to like so:

public class Config
{
    @Value("${my.value}")
    private String mValue;

    @Value("${another.value}")
    private String mAnotherValue;

    // More below...
}

The problem is that, mValue and mAnotherValue are ALWAYS null... yet in my Controllers, the value is being loaded just fine. What gives?

DigitalZebra
  • 39,494
  • 39
  • 114
  • 146

8 Answers8

71

If instances of Config are being instantiated manually via new, then Spring isn't getting involved, and so the annotations will be ignored.

If you can't change your code to make Spring instantiate the bean (maybe using a prototype-scoped bean), then the other option is to use Spring's load-time classloader weaving functionality (see docs). This is some low-level AOP which allows you to instantiate objects as you normally would, but Spring will pass them through the application context to get them wired up, configured, initialized, etc.

It doesn't work on all platforms, though, so read the above documentation link to see if it'll work for you.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Hey skaffman, thanks for the advise... The issue is that I'm instantiating `Config` from a JSP. I'm not sure if I can find a way around that? Maybe by using some sort of factory? – DigitalZebra Nov 09 '10 at 16:12
  • @Polaris878: My suggestion should still work for JSP-instantiated objects (any any object instantiated via reflection) – skaffman Nov 09 '10 at 16:50
  • +1. I seize to know about Spring in that detail. Didn't touch it for years. Stupid workplace. – Adeel Ansari Nov 10 '10 at 02:25
  • +1 Oh my... this saves me! Was trying to read property files and wasn't sure why @Value was giving null – Vladtn Jan 24 '13 at 13:13
  • I tried all the guides, checked config like 50 times, used all the possible annotations.. Nearly went crazy, and then I decided to test if a controller sees the property.. Yes! And the service - too! In my case, that was actually the object used in the service initialized with `new` :D – Katia Savina Mar 07 '21 at 17:19
  • 1
    Thanks, that was it. I was trying to instantiate a new object rather than autowire the class and let Spring do the work. – rudyg123 Apr 07 '22 at 22:57
42

I had similar issues but was a newbie to Spring. I was trying to load properties into an @Service, and tried to use @Value to retrieve the property value with...

@Autowired
public @Value("#{myProperties['myValue']}") String myValue;

I spend a whole day trying various combinations of annotations, but it always returned null. In the end the answer as always is obvious after the fact.

1) make sure Spring is scanning your class for annotations by including the package hierachy In your servlet.xml (it will scan everything below the base value you insert.

2) Make sure you are NOT 'new'ing the class that you just told Spring to look at. Instead, you use @Autowire in the @Controller class.

Everything in Spring is a Singleton, and what was happening was Spring loaded the values into its Singleton, then I had 'new'ed another instance of the class which did not contain the newly loaded values so it was always null.

Instead in the @Controller use...

@Autowired
private MyService service; 

Debugging... One thing I did to find this was to extend my Service as follows...

@Service
public class MyService implements InitializingBean 

Then put in debug statements in...

@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
LOGGER.debug("property myValue:" + myValue);        
}

Here I could see the value being set on initialization, and later when I printed it in a method it was null, so this was a good clue for me that it was not the same instance.

Another clue to this error was that Tomcat complained of Timeouts trying to read from the Socket with Unable to parse HTTPheader... This was because Spring had created an instance of the service and so had I, so my one was doing the real work, and Spring was timing out on its instance.

Rob
  • 421
  • 4
  • 2
  • 1
    what is myProperties here. could you please explain. – SYMA Jul 03 '18 at 15:44
  • It was #2 in my case. Switching from declaration as `new` to `@Autowired` solved my problem. – JackTheKnife Mar 12 '19 at 17:31
  • I can confirm the properties are being set in my `@Service`class, however when I try to reference those fields by pulling it into another class via `@Autowired private MyService myservice`, those fields are now null. Why is this happening? – fudge Mar 29 '23 at 04:10
  • i had to add "@ConfigurationProperties" to a configclass (no instantiations of that class with "new") then you can "@Autowire" that class and annotated "@Value" fields "work" – Bender Jun 25 '23 at 10:23
6

See my answer here.

I ran into the same symptoms (@Value-annotated fields being null) but with a different underlying issue:

import com.google.api.client.util.Value;

Ensure that you are importing the correct @Value annotation class! Especially with the convenience of IDEs nowadays, this is a VERY easy mistake to make (I am using IntelliJ, and if you auto-import too quickly without reading WHAT you are auto-importing, you might waste a few hours like I did).

The correct import is:

org.springframework.beans.factory.annotation.Value

scottysseus
  • 1,922
  • 3
  • 25
  • 50
4

As its working with @Controller, it seems you are instantiating Config yourself. Let the Spring instantiate it.

Adeel Ansari
  • 39,541
  • 12
  • 93
  • 133
  • Ahhh yeah that is the problem... I'm calling the constructor myself. How then, could I access these properties? If I can't access these properties, that basically means I need to find some other way to load configuration, as the Spring properties loader just won't cut it if those properties are only available in certain scenarios. – DigitalZebra Nov 09 '10 at 05:04
  • @Polaris878: Let the Spring instantiate it. Do we have any issues with that? Or do it yourself using `ResourceBundle`. – Adeel Ansari Nov 09 '10 at 05:07
3

You can also make your properties private, make sure your class is a Spring bean using @Service or @Component annotations so it always gets instantiated and finally add setter methods annotated with @Value . This ensures your properties will be assigned the values specified in your application.properties or yml config files.

@Service
public class Config {
    private static String myProperty;

    private static String myOtherProperty;

    @Value("${my.value}")    
    public void setMyProperty(String myValue) {
    this.myProperty = myValue;}

    @Value("${other.value}")
    public void setMyOtherProperty(String otherValue) {
    this.myOtherProperty = otherValue;}

    //rest of your code...
}
Keiv
  • 171
  • 1
  • 5
  • I think that maybe since your fields are `static`, you were running into a [similar issue](https://stackoverflow.com/a/76309224/1357094) that I did. – cellepo May 22 '23 at 19:07
  • in other words: if your fields were not static, you might be able to do field annotation with `@Value`, instead of your method-level annotation (and maybe not even need setter methods altogether). – cellepo May 22 '23 at 19:15
2

Add <context:spring-configured /> to you application context file.

Then add the @Configurable annotation to Config class.

kabal
  • 2,085
  • 4
  • 29
  • 43
1

Try to make sure the fields on your Bean, that want to be instantiated by @Value, are NOT static (instead are instance). I think something to do with the Spring Context loaded, needed for the Annotations to work, might not happen by the time of static initialization (and instead needs to have an instance loaded into Spring Context).

This:

@Value("${my.property.name}")
String myProperty

NOT this:

@Value("${my.property.name}")
static String myProperty
cellepo
  • 4,001
  • 2
  • 38
  • 57
0

In my case in my unit test, executeScenarioRequest always is null

@RunWith(SpringRunner.class)
@ExtendWith(MockitoExtension.class)
class ScenarioServiceTestOld {

    @Value("classpath:scenario/SampleScenario.json")
    Resource executeScenarioRequest;

Change @ExtendWith(MockitoExtension.class) to @ExtendWith(SpringExtension.class)

JoSSte
  • 2,953
  • 6
  • 34
  • 54
BabyishTank
  • 1,329
  • 3
  • 18
  • 39