66

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?

5
  • Is Config class defined as a spring bean? Commented Nov 9, 2010 at 4:28
  • no? How would I do that with annotations? Commented Nov 9, 2010 at 4:39
  • 1
    I tried using "@Component" and "@Controller" on the class and neither worked Commented Nov 9, 2010 at 4:57
  • How its working in Controller then? What's different in your Controller? Commented Nov 9, 2010 at 4:58
  • That is what is so perplexing to me... the Config class is in the same package and everything... the only difference is that with my Controller that works I actually have a request mapping. Commented Nov 9, 2010 at 5:00

8 Answers 8

77

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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?
@Polaris878: My suggestion should still work for JSP-instantiated objects (any any object instantiated via reflection)
+1. I seize to know about Spring in that detail. Didn't touch it for years. Stupid workplace.
+1 Oh my... this saves me! Was trying to read property files and wasn't sure why @Value was giving null
Thanks, that was it. I was trying to instantiate a new object rather than autowire the class and let Spring do the work.
|
45

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.

4 Comments

what is myProperties here. could you please explain.
It was #2 in my case. Switching from declaration as new to @Autowired solved my problem.
I can confirm the properties are being set in my @Serviceclass, 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?
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"
9

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

3 Comments

Great finding! In my case, I was overshading the regular spring boot dependencies with "org.springframework.boot" plugin
what is the correct annotation? this should be provided in an answer
I added the correct import to the answer
4

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

2 Comments

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.
@Polaris878: Let the Spring instantiate it. Do we have any issues with that? Or do it yourself using ResourceBundle.
4

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...
}

2 Comments

I think that maybe since your fields are static, you were running into a similar issue that I did.
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).
3

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

Comments

2

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

Then add the @Configurable annotation to Config class.

Comments

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)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.