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.
Related
I am trying bind properties from yml file to object by using inheritance but getting null values in binding.
I have three classes, StoreConfig is superclass, MessageStoreConfig is subclass and MongodbMessageStore is also subclass of MessageStoreConfig.
#ConfigurationProperties(prefix = "store")
public class StoreConfig {}
#ConfigurationProperties(prefix = "messagestore")
public class MessageStoreConfig extends StoreConfig {}
#Component
#ConfigurationProperties(prefix = "config.mongodb")
public class MongoDbMessageStoreConfig extends MessageStoreConfig {
#Value("${connection_string}")
private String connectionString;
public String getConnectionString() {
return connectionString;
}
public void setConnectionString(String connectionString) {
this.connectionString = connectionString;
}
}
application.yml file
store:
messagestore:
config:
mongodb:
connection_string: mongodb://localhost:27017
I am getting below error.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'connection_string' in value "${connection_string}"
Am i doing the right thing or spring does not support this kind of binding?
Spring does not work this way. First of all you should not mix #ConfigurationProperties and #Value. They are two different ways of importing configuration from properties. So even if you annotated your class like this:
#ConfigurationProperties(prefix = "store.messagestore.config.mongodb")
and then use #Value("${connection_string}") it still would not work because #Value and #ConfigurationProperties can not be mixed.
Similar question is discussed here: https://github.com/spring-projects/spring-boot/issues/2254
Also extending a class which is annotated with #ConfigurationProperties with another class which is also annotated with #ConfigurationProperties does not mean that prefixes will be concatenated together. This means that Spring expects multiple sets of properties from you (ones of the superclass and ones of the subclass) and they are not related to each other at all.
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();
}
}
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
#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
}
I have a spring controller that uses annotations. I gave this controller a constructor that takes two arguments. I want both ways of initializing the controller: constructor injection and setter injection.
#Controller("viewQuestionController")
#RequestMapping("/public/viewQuestions")
public class ViewQuestionController
{
#Resource(name="questionService")
private QuestionService questionService;
/*public ViewQuestionController()
{
int i=0;
i++;
}
*/
public ViewQuestionController(#Qualifier("questionService") QuestionService questionService)
{
this.questionService = questionService;
}
#Resource(name="questionService")
public void setQuestionService(QuestionService questionService)
{
this.questionService = questionService;
}
}
When I uncomment the default constructor, the controller is initiated correctly. However, if I don't, I get a BeanInstantiationException, No default constructor found; nested exception is java.lang.NoSuchMethodException.
So, is my configuration for the annotated constructor wrong or does a completely annotated controller in spring always need a default constructor?
If you want to configure constructor injection via annotations, you need to put the corresponding annotation on the constructor. I'm not sure how it can be done with #Resource, but #Autowired and #Inject support it:
#Autowired
public ViewQuestionController(#Qualifier("questionService") QuestionService questionService)
or
#Inject
public ViewQuestionController(#Named("questionService") QuestionService questionService)
I think Controller beans need a default constructor as they are initialized by the framework but there is no way to tell the framework hot to provide the dependency.
On second thought why not you autowire your question service and Spring will take care of it.
The following code should be good
#Controller("viewQuestionController")
#RequestMapping("/public/viewQuestions")
public class ViewQuestionController
{
#Autowired
private QuestionService questionService;
//Not providing any constructor would also be fine
public ViewQuestionController(){}
questionService will be initialized properly by Spring