Reputation: 566
Can anyone help me with a Spring Boot problem?
I want to create a factory bean as part of my application context but I want to be able to instantiate it with injected property values. However it seems that Spring will load FactoryBeans before anything else as demonstrated here:
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
@EnableAutoConfiguration
public class TestClass
{
@Value("${test.value}")
String value;
@Bean
public Object test1()
{
System.out.println("test.value=" + value );
List<String> list = new ArrayList<String>();
ListFactoryBean factory = new ListFactoryBean();
factory.setSourceList(list);
return factory;
}
public static void main(String[] args)
{
SpringApplication.run(TestClass.class, args);
}
}
When run with
java -Dtest.value=HELLO -jar myTest.jar
It loads in the value correctly:
test.value=HELLO
However, when I specify that the bean to be loaded is in fact a factory bean, and run it in the same way:
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
@EnableAutoConfiguration
public class TestClass
{
@Value("${test.value}")
String value;
@Bean
public AbstractFactoryBean test1()
{
System.out.println("test.value=" + value );
List<String> list = new ArrayList<String>();
ListFactoryBean factory = new ListFactoryBean();
factory.setSourceList(list);
return factory;
}
public static void main(String[] args)
{
SpringApplication.run(TestClass.class, args);
}
}
The value is null because it hasn't been injected yet.
test.value=null
Is there any way around this?
Thanks
Upvotes: 1
Views: 11054
Reputation: 31595
Take look at Empowering your apps with Spring Boot's property support
There is new annotation @EnableConfigurationProperties
in Spring Boot Actuator
The Spring Environment is a collection of name-value pairs taken from (in order of decreasing precedence)
1) the command line,
2) the external configuration file,
3) System properties,
4) the OS environment.
There is also possible to define application properties (external configuration) in YAML format.
Upvotes: 0
Reputation: 58134
Spring often has to query bean definitions for the type of object they produce. Factory beans are always problematic because they can cause dependency cascades in a futile attempt to resolve all dynamic information available before asking for the type.
I think ListFactoryBean
is insufficiently precise about its product type (getObjectType()
can only return a non-generic List.class
). You might be able to write your own factory that is parameterized with the correct generic type. Or you might get away with just declaring the @Bean
to return a FactoryBean<List<String>
.
Another tip is to move the @Bean
definition to a separate class (e.g. a nested static one) so that it can be instantiated independently of the rest of the application context. E.g.
@EnableAutoConfiguration
public class TestClass
{
protected static class NestedConfiguration {
@Value("${test.value}")
String value;
@Bean
public FactoryBean<Properties> test1()
{
System.out.println("test.value=" + value );
// ...
return factory;
}
}
...
}
Not really a Boot question this one so you might consider changing the tags.
Upvotes: 3