1

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?

1 Answer 1

4

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.

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

2 Comments

That makes perfect sense in this particular case, but I also would like to be able to set a default value on an endpoint that would accept JSON as input. The JSON would not necessarily contain the entire object in this example it would it might contain only { "id":0,"content":"foo"} I would like Jackson to just set the dooDad value using the @Value. If I understand what you are saying though I would have to test the value of dooDad in the endpoint and set it to @Value if it is null? Isn't there another way?
@MichaelHatch Absolutely. In that scenario we'll change the RequestMethod to POST, and set a default value in the RequestParam (as you've done with the name parameter) This is the cleanest approach. If no value is submitted for dooDad then it will use the default. Now if you are insist on setting this default value from within a properties file, it gets a little more verbose but it can be done. See my example repo for details: github.com/kanderson450/stackoverflow-q40271433 The master branch is the simple approach, but check the feature branch for the properties file approach.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.