Michael Hatch
Michael Hatch

Reputation: 45

Accessing Spring Boot application.properties using the @Value annotation

I have read through just about everything I can find and for some reason I cannot quite wrap my brain around how to make this work. After struggling in an existing project for 2 days I downloaded the Getting Started Content for Rest from Spring.io

https://spring.io/guides/gs/rest-service/

I created a resources folder in the main folder and added a file called application.properties with the following:

my.setting = HELLOTHERE

I then modified the Greeting class as follows:

    package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
public class Greeting {

    private long id;
    private String content;
    private String dooDad;
    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public Greeting() {
        // TODO Auto-generated constructor stub
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }

    public String getDooDad() {
        return dooDad;
    }
    @Autowired
    public void setDooDad(@Value("${my.setting}")String dooDad) {
        this.dooDad = dooDad;
    }
}

I modified the Application class to have the @Configuration annotation as follows:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
@Configuration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

I didn't modify the GreetingController class but will include it for completeness:

package hello;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}

When I run the application and look at the output I expected to see a value for dooDad, instead it is always null.

{"id":1,"content":"Hello, mike!","dooDad":null}

Any help in getting this figured out would be appreciated. I did discover that if I put the @Value annotation on a variable in the Application class it would populate that variable with the correct value, just not when it is in the Greeting Class?

Upvotes: 1

Views: 4719

Answers (1)

Kyle Anderson
Kyle Anderson

Reputation: 7051

You are bypassing dependency injection by instantiating the object Greeting yourself:

return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));

Thus the @Component and @Value annotations within the Greeting object aren't doing anything (for this particular instance).

I would suggest doing the following:

  1. Make the Greeting object a standard POJO
  2. Include String dooDad in the constructor
  3. Use @Value("${my.setting}") String dooDad as a field in your Greeting Controller
  4. Pass dooDad into your new Greeting object like so:

    return new Greeting(counter.incrementAndGet(), String.format(template, name), dooDad);

Update: Added example project to illustrate various options for achieving the setup described by the OP.

Upvotes: 4

Related Questions