Ashwini
Ashwini

Reputation: 391

Autowired not working for ConfigurationProperties bean in Spring

I have an application.yml file that has the list of objects as below:

outlook:
  mailboxes:
    - id: m1
      name: mailbox1

    - id: m2
      name: mailbox2

I have created a spring configuration class called MailBoxProperties to have these properties in a bean as below:

MailBoxProperties.java

@ConfigurationProperties(prefix = "outlook")
@Configuration
public class MailBoxProperties {

    private List<MailBox> mailboxes;

    public MailBoxProperties() {

    }

    public MailBoxProperties(List<MailBox> mailboxes) {
        this.mailboxes = mailboxes;
    }

    public void setMailBoxes(List<MailBox> mailboxes) {
        this.mailboxes = mailboxes;
    }

    public List<MailBox> getMailBoxes() {
        return mailboxes;
    }

    public static class MailBox {
        public String getName() {
            return this.name;
        }

        public String getId() {
            return this.id;
        }


        private String id, name;

        public MailBox() {

        }

        public MailBox(String id, String name) {
            this.id = id;
            this.name = name;
        }
    }
}

I would like to inject the above config bean into another config class as below:

OutlookConnectionManager.java

@Configuration
@EnableConfigurationProperties
public class OutlookConnectionManager{
  @Autowired
    private MailBoxProperties mailBoxProperties;

    private List<String> names;

    @Bean
    public OutlookConnectionManager getOutlookConnectionManager() {
        OutlookConnectionManager outlookConnectionManager = new OutlookConnectionManager();
        outlookConnectionManager.getMailBoxProperties();
        return outlookConnectionManager;
    }

    public void getMailBoxProperties() {
        names = new ArrayList<String> ();
        for(MailBox mail: mailBoxProperties.getMailBoxes()) {
              this.names.add(mail.getName());
        }
    }
}

However, MailBoxProperties is always null and throws null pointer exception on calling mailBoxProperties.getMailBoxes() from getMailBoxProperties.

(I have tried giving @Configuration @EnableConfigurationProperties(MailBoxProperties.class). It throws an exception stating that 2 beans are created. One with the actual path and the other with null). I have tried @Import(){MailBoxProperties.class}.

But, the injection perfectly works in my main application class as below: It fetches the bean and prints the mailbox name correctly.

MailApplication.java

@SpringBootApplication
public class MailApplication {

    public static void main(String[] args) {
        ApplicationContext context = new SpringApplicationBuilder(MailApplication.class).run(args);
MailBoxProperties props = context.getBean(MailBoxProperties.class);
props. getMailBoxes()
                .forEach(cc -> System.out.println(cc.getName()));

}
}

Am I missing any configuration in OutlookConnectionManager.java ? Please help.

Note: I am using Spring boot 1.5.7.RELEASE

Thank you.

Upvotes: 2

Views: 10403

Answers (3)

varren
varren

Reputation: 14731

I think there are 2 things you can fix:


First you have some strange getter and setter names in your MailBoxProperties (like setMailBoxes for field mailboxes should be setMailboxes) and i don't think MailBoxProperties will take values from yaml file with custom setter names. Use default getters and setters

@ConfigurationProperties("outlook")
public class MailBoxProperties {

    private List<MailBox> mailboxes;

    public List<MailBox> getMailboxes() {
        return mailboxes;
    }

    public void setMailboxes(List<MailBox> mailboxes) {
        this.mailboxes = mailboxes;
    }

    public static class MailBox {
        private String id, name;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

Second, i don't know what is your goal in OutlookConnectionManager, but as @M. Deinum mentioned you are trying to call getMailBoxProperties before spring has had a chance to auto wire the fields.

And i think you can achieve similar behavior with

@Component
public class OutlookConnectionManager{

    private final MailBoxProperties mailBoxProperties;

    private List<String> names;

    @Autowired
    public OutlookConnectionManager(MailBoxProperties mailBoxProperties) {
        this.mailBoxProperties = mailBoxProperties;
        this.names = getNames();
    }


    public List<String> getNames() {
        List<String> names = new ArrayList<>();
        for(MailBoxProperties.MailBox mail: mailBoxProperties.getMailboxes()) {
            names.add(mail.getName());
        }
        return names;
    }
}

Upvotes: 0

pleft
pleft

Reputation: 7905

Basically your MailBoxProperties should be as @varren suggested. And there is no need to annotate with @Configuration this properties class.

So :

@ConfigurationProperties(prefix = "outlook")
public class MailBoxProperties {

    private List<MailBox> mailboxes;

    public List<MailBox> getMailboxes() {
        return mailboxes;
    }

    public void setMailboxes(List<MailBox> mailboxes) {
        this.mailboxes = mailboxes;
    }

    public static class MailBox {
        private String id, name;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

Then you can keep your OutlookConnectionManager annotated with @Configuration but what are you trying to do inside it is a little anorthodox (you are re-instantiating the same configuration class with the new keyword).

However you could make it work by changing your @Bean method to:

OutlookConnectionManager:

@Bean
public OutlookConnectionManager getOutlookConnectionManager() {
    OutlookConnectionManager outlookConnectionManager = new OutlookConnectionManager();
    //this call will set this.names
    this.getMailBoxProperties();
    //set this.names to the names variable of outlookConnectionManager instance
    outlookConnectionManager.names = this.names;
    return outlookConnectionManager;
}

Upvotes: 2

Adam
Adam

Reputation: 2269

As @m-deinum pointed out, the properties are still null. Try doing the initialization in a method annotated with @PostConstruct. All autowired fields should be populated by that point.

More details about @PostConstruct here: How to call a method after bean initialization is complete?

Upvotes: 0

Related Questions