I have a #Builder class and one of the variables used in that class needs to be configurable. I've tried
#Value("${string.propertiesFile}")
private String stringValue;
where I'm inserting the a value defined in application.properties file into my stringValue variable. However, stringValue is always null.
How can I add a inject a literal value into my #Builder Class?
Thanks so much!
As far as I understand for the #Value annotation to work, spring needs to be in control of the bean creation process so that it can inject the value . #Value can be used for injecting values into fields in Spring-managed beans and it can be applied at the field or constructor/method parameter level. When using the lombok #Builder annotation you are essentially implementing the builder pattern. So spring is not in control of the bean creation hence you cannot inject the value directly into your class .
Instead what you could do is have the value injected using #Value in another spring-managed class from where you are using this builder and probably pass in the value to the builder like :
#Service
public SomeServiceClassImpl {
#Value("${string.propertiesFile}")
private String stringValue;
public void someMethod() {
BuilderPatternClass testBuilderPatternClass = BuilderPatternClass.builder()
.name(stringValue)
.id(1)
.build();
}
}
Related
From spring boot documentation, #ConfigurationProperties will
generate your own configuration metadata file from items annotated
with #ConfigurationProperties
I tried use #Configuration and #ConfigurationProperties separately on my configuration class.
#Component
//#Configuration
#ConfigurationProperties
#EnableSpringDataWebSupport
#EnableAsync
public class AppConfig {
...
}
I didn't see any noticable difference.
What's the usage of #ConfigurationProperties or #Configuration?
#Configuration is used to create a class the creates new beans (by annotating its methods with #Bean):
#Configuration
public class CustomConfiguration {
#Bean
public SomeClass someClass() {
return new SomeClass();
}
}
#ConfigurationProperties binds external configuration into the fields of the class which it annotates. It's common to use it with a #Bean method to create a new bean that encapsulates configuration which can be controlled externally.
Here's a real world example of how we've used it. Consider a simple POJO that holds some values related to connecting to ZooKeeper:
public class ZookeeperProperties
{
private String connectUrl;
private int sessionTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int connectTimeoutMillis = (int) TimeUnit.SECONDS.toMillis(15);
private int retryMillis = (int) TimeUnit.SECONDS.toMillis(5);
private int maxRetries = Integer.MAX_VALUE;
// getters and setters for the private fields
}
Now we can create a bean of type ZookeeperProperties and automatically populate it using external configuration:
#Configuration
public class ZooKeeperConfiguration {
#ConfigurationProperties(prefix = "zookeeper")
#Bean
public ZookeeperProperties zookeeperProperties() {
// Now the object we create below will have its fields populated
// with any external config that starts with "zookeeper" and
// whose suffix matches a field name in the class.
//
// For example, we can set zookeeper.retryMillis=10000 in our
// config files, environment, etc. to set the corresponding field
return new ZookeeperProperties();
}
}
The benefit of this is that it's less verbose than adding #Value to every field of ZookeeperProperties. Instead, you provide a single annotation on the #Bean method and Spring automatically binds any external configuration it finds with the matching prefix to the fields of that class.
It also lets different users of my class (i.e. anyone who creates a bean type of ZookeeperProperties) use their own prefix to configure the class.
The use case of ConfigurationProperties is for externalizing configuration.
#Configuration
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
#ConfigrationProperties
-- Is added to a class definition or a #Bean method in a #Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
See the screenshot to differentiate #Value from #ConfigurationProperties.
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
Is there a way to filter by property prefix when reading in values using #Value annotation in Camel? I'm using the BridgePropertyPlaceholderConfigurer to specify the property files and I've tried setting #ConfigurationProperties(prefix = "SlowEndpoint") on the property bean, but it seems to ignore it.
ConfigurationProperties and Value annotations were not meant to be used together in this scenario. After removing #Value annotations, the ConfigurationProperties annotation picked up properties that were named identical to the property bean members.
public class SlowEndpointConfiguration extends BaseHystrixConfigurationDefinition {
#Configuration
#Profile({"default", "local"})
#PropertySource("classpath:/properties/local/hystrix/SlowEndpointHystrix.properties")
#ConfigurationProperties(prefix = "SlowEndpoint")
static class Defaults extends SlowEndpointConfiguration{
}
}
#Component
#PropertySources({ #PropertySource("classpath:mail.properties") })
public class A implements B {
#Value("${mail.team.address}")
private String teamAddress;
// has getter and setters .not shown for brevity.
Now when i call the class i get the value of teamAddress as NULL .But in the property file mail.team.address has some value.
My property file is present under src/main/resource folder
Making a call
A a = new A ();
a.someMethodinClassA();
You can not create instance of class by yourself when you want Spring to resolve #Value annotation.
See documentation:
Note that actual processing of the #Value annotation is performed by a BeanPostProcessor which in turn means that you cannot use #Value within BeanPostProcessor or BeanFactoryPostProcessor types. Please consult the javadoc for the AutowiredAnnotationBeanPostProcessor class (which, by default, checks for the presence of this annotation).
Simple solution for you: just annotate class with any #Component annotation and let Spring to create an instance of your class.
You can't create (with a "new" keywoard) for spring bean. If you do it like this, spring doesn't participate in the object creation and configuration, which means that there is no autowiring, the bean is not in Application Context, etc. And of course, #Value annotation won't be processed among other things
The better way is to inject the class A to the code that you used in your example:
A a = new A ();
a.someMethodinClassA();
Show become:
#Component
public class SomeClass {
private final A a;
public SomeClass(A a) {
this.a = a;
}
public void foo() {
a.someMethodinClassA();
}
}
You should read some basics around spring dependency injection. The class that you have autowired with #Component is scanned via component scanning and its object is created by spring container for you.
that is the reason you should not create the object yourself using new keyword.
wherever in your new class you want to use your class A object you can autowire it as below:
#Component
public class TestC{
private A a; // this object will be injected by spring for you
}
Basically I have configuration in property file
data.enabled = true
and I have added a POJO class for that
#Configuration
#PropertySource(value = {"classpath:dataconfig.properties"})
public class DataProperties {
private Boolean enabled;
}
and I want to check the enabled property on html tag using thymeleaf.
<li th:if="${DataProperties.getEnabled() == true}"><h3>Hello</h3></li>
First you should add #ConfigurationProperties(prefix="data") to your configuration class.
#Configuration
#PropertySource("classpath:dataconfig.properties")
#ConfigurationProperties(prefix="data")
public class DataProperties {
private Boolean enabled;
This activates that your variable is directly binded to the property value without using #Value annotation. In your property file you have data.enabled. This means that your prefix is data. So you have to set this, too.
To use the bean in thymeleaf directly you need to use a special command. In your case it should look like that: (see point 5 in the docs)
<li th:if="${#dataProperties.getEnabled() == true}" ><h3>Hello</h3></li>
Addition 1:
To use the same property in other spring beans like controllers you have to autowire your DataProperties
#Controller
public class IndexController {
#Autowired
private DataProperties dataProperties;
#GetMapping("/")
public String index() {
dataProperties.getEnabled();
return "index";
}
Just to mention it autowire on a field is bad practice. But its your choice to use it like that or autowire on constructor or setter.
I have an abstract class in which I am trying to use the #Value annotation to inject value from a property file
public abstract class Parent {
#Value ("${shared.val}")
private String sharedVal;
public Parent() {
//perform common action using sharedVal
}
}
#Component
public class ChildA extends Parent {
Param a1;
#Autowired
public ChildA (Param a1) {
super();
this.a1 = a1;
}
}
I am getting NullPointerException since sharedVal is not set. I tried adding #Component stereotype on the abstract class and still the same thing.
Can I inject value into abstract class this way? If not how can accomplish this?
I think you'll find the sharedVal is being set, but you're trying to use it too soon in the constructor. The constructor is being called (must be called) before Spring injects the value using the #Value annotation.
Instead of processing the value in the constructor, try a #PostContruct method instead, eg:
#PostConstruct
void init() {
//perform common action using sharedVal
}
(or alternatively, implement Spring's InitializingBean interface).
Can I inject value into abstract class this way?
Abstract classes cannot be instantiated, so nothing can be injected into the abstract class. Instead , you should inject the value to its concrete subclass.
Make sure your concrete subclass is marked as #Component stereotype and being "component scanning" by Spring . #Component on the abstract class is not needed as it cannot be instantiated.
Update : I finally figure out that you are trying to access the injected value inside the constructor but found that the value is not set. It is because Spring will inject the value after the bean is instantiated . So if constructor injection is not used , the injected value cannot be accessed inside the constructor . You can use #PostContruct or implementing InitializingBean as suggested by Matt.
Following shows if XML configuration is used :
<context:property-placeholder location="classpath:xxxxx.properties" ignore-unresolvable="true" />
<bean id="parent" class="pkg.Parent" abstract="true" init-method="postConstruct">
<property name="sharedVal" value="${shared.val}" />
</bean>
<bean id="child" class="pkg.ChildA" parent="parent">
Perform your common action using sharedVal inside Parent#postConstruct()