Reputation: 75
I'm trying to bind a list of objects to my entity class using @ConfigurationProperties annotation. Spring Boot framework seems to ignore that annotation, and it literally does nothing.
Here is my application.yml properties file:
spring:
datasource:
url: jdbc:mysql://localhost:3306/search-engine
username: landsreyk
password: 12345678
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
show-sql: false
hibernate:
ddl-auto: none
sites:
- url: http://someurl1.com
name: somename1
- url: https://someurl2.com
name: somename2
- url: https://someurl3.com
name: somename3
And here is my entity class:
package main.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.sql.Timestamp;
@Getter
@Setter
@ToString
@Entity
@Table(name = "_site")
public class Site {
@Column(nullable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private Status status;
@Column(name = "status_time")
private Timestamp statusTime;
@Column(name = "last_error")
private String lastError;
private String url;
private String name;
public enum Status {
INDEXING, INDEXED, FAILED
}
}
Binding is pretty straight forward:
package main.utilities;
import lombok.Getter;
import lombok.Setter;
import main.model.Site;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties
public class ApplicationProperties {
@Getter
@Setter
private List<Site> sites;
}
Somewhere in application I'm testing that binding, let's say I created end point in my API to test that binding, i.e. my controller calls a method which just prints all objects in a list:
package main.application.indexer;
import main.utilities.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
public class IndexBuilder {
@Autowired
private ApplicationProperties properties;
public void run() {
System.out.println(properties.getSites());
}
}
Expected:
After launch, ApplicationProperties instance is not null. Calling properties.getSites() returns a list of Site objects. Each Site object has url and name fields initialized from yaml source.
Actual:
After launch ApplicationProperties instance is null.
I was shocked to realize that Spring wasn't able to accomplish such a simple binding. Knowing that just parsing a yaml file is not such a hard task to accomplish, I thought that Spring Framework should have this feature build-in. How do I bind my list?
By the way, here is my project structure.
I edited IndexBuilder class. Added @Configuration and @Bean annotations. Now it looks like this:
package main.application.indexer;
import main.utilities.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class IndexBuilder {
@Autowired
private ApplicationProperties properties;
@Bean
public void run() {
System.out.println(properties.getSites());
}
}
That fixed the problem with initialization, but Spring Framework just call run() immideately after launch. That isn't an expected behavior.
Answer of this user is completely authentic. He is right.
Upvotes: 2
Views: 629
Reputation: 75
Turns out, Spring isn't that smart as I thought.
To access @ConfigurationProperties class, that is: accessing a bean from a non-managed-by-spring class, - I had to go through this article:
Autowiring Spring Beans Into Classes Not Managed by Spring https://dzone.com/articles/autowiring-spring-beans-into-classes-not-managed-by-spring
It solved the problem.
Upvotes: 1
Reputation: 10964
Instead of using @Configuration
you should use @Component
on your IndexBuilder
class.
@Configuration
should only be used for configuration classes that define the application‘s beans. Here it is actually expected that the methods annotated with @Bean
are executed immediately on application start. This is the phase where spring creates all needed beans. However I’m wondering spring accepts a void
method with the @Bean
annotation.
What’s your expected time when the run method should be executed? With a normal bean/component/service you could for example use the @PostConstruct
annotation on methods. These will however be executed in the very same phase as the @Bean
methods or at least not much later.
Upvotes: 3