SpringBoot does not resolve #Value properties without PropertySource annotation - spring-boot

I have a bunch of #Value annotated fields in a SpringBoot configuration file, with the matching values in the standard application.properties . If I don't annotate the configuration file with #PropertySource("classpath:application.properties") it will just copy the "${prop1}" string into the actual variable.
I tried adding #EnableAutoConfiguration to the #Configuration class (instead of the PropertySource annotation), but all it does is to break when a requested property is not found.
SpringBoot is supposed to resolve the properties automatically from the standard application.properties file, why this behaviour? Using version 2.2.2.RELEASE
Update:
The answers are correct, the reason it was not working was that I was calling these properties in a test. Annotating the test with #SpringBootTest fixes the issue. In fact when the application is running it is #SpringBootApplication that does the magic

As you can read in this article (chapter 5), SpringBoot manage automatically the application.properties file.
I don't know if this is your problem because I've not seen the code, but in Spring Boot the Application class should be annotated with #SpringBootApplication.
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Take a look at this starting example.
You can then inject the value for example in a controller class in this way:
#RestController
public class HelloController {
#Value("${test}")
private String test;
#RequestMapping("/test")
String hello() {
return test;
}
}

Related

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

Spring boot #ConfigurationProperties not working

I'm using #ConfigurationProperties for auto configuration of properties. My code is working in IDE. But when I run the jar in command line, it is not working.
Configuration class:
#Configuration
#ConfigurationProperties(prefix="location")
public class Location {
private String base;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
}
Main class:
#SpringBootApplication
#EnableConfigurationProperties(Location.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
application.yml:
location:
base: c:\test
If I autowire Location class, I see an instance created. But there is not property set. The code is not entering setBase() method.
The application prints this in the console.
AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject'
annotation found and supported for autowiring
Make sure that application.yml file is in the root of your classpath, usually it's put in the resources folder.
The fact that the setBase() method is not called suggests that your application.yml file is not being found. Spring looks in the root of your classpath for the application.yml file.
The comment from M. Deinum is correct saying that your duplicated annotations will result in 2 spring beans for Location class. However, as you say you managed to autowire the bean without getting an error it suggests that your Location class isn't in a package that is found by spring when it's scanning for beans. If there were 2 beans then you'd get an error when you autowired it. By default spring will scan use the package where the #SpringBootApplication is as the base. It will then look in this package and all sub packages.
If your package structure is like this...
myapp.main
Application.java
myapp.config
Location.java
Then you need to add scanBasePackages="myapp" to the #SpringBootApplication annotation.
Also change your main class and remove the #Enable.. annotations. i.e.:
#SpringBootApplication(scanBasePackages="myapp")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As nothing worked with yaml, I had to change to property file and use
#PropertySource({"classpath:application.properties"})
for the spring to identify the properties file

#EnableAutoConfiguration annotation with class prameter is not initializing properties object

i have following #CongfigurationProperties class
//#Component
#ConfigurationProperties
#PropertySource("classpath:typesofcharge.properties")
public class ChargeProperties {
private HashMap<String,String> charge=new HashMap<>();
public HashMap<String,String> getCharge()
{
return this.charge;
}
}
And this is my Configuration file
#SpringBootApplication
#ComponentScan({"com.vehiclemanagement.config,com.vehiclemanagement.client,"
+ "com.vehiclemanagement.controller,"
+ "com.vehiclemanagement.exception,"
+ "com.vehiclemanagement.model,"
+ "com.vehiclemanagement.service"})
#EnableConfigurationProperties(ChargeProperties.class)
public class VehicleManagementConfig {
public static void main(String[] args) {
SpringApplication.run(VehicleManagementConfig.class, args);
}
}
If i use #Component annotation in ChargeProperties and remove ChargeProperties.class annotation in Configuration class the charge HashMap is initialized properly
If i remove #Component and pass ChargeProperties.class as argument like this
#EnableConfigurationProperties(ChargeProperties.class) like how document says the charge HashMap is empty when i run
I am using spring boot 2.0.2 release .But i am following latest docs. Can anyone explain why this are not working as document suggest
content of property file is as follows
UPDATE the content of property files are as shown
#DO NOT MODIFY THIS FILE
charge.peak=Double_rate;
charge.lateNight=duration_based_charge;
charge.earlyMorning=special_offers;
When specifying ChargeProperies.class on the #EnableConfigurationProperties annotation it will be registered as a bean through the EnableConfigurationPropertiesImportSelector class inside #EnableConfigurationProperties.
So in the example, if you have only annotated the ChargeProperties class with #ConfigurationProperties it will create a chargeProperties bean with an empty charge HashMap because it defaulted back to application.properties as the source.
A custom source can be specified by using #PropertySource.
#PropertySource annotation providing a convenient and declarative mechanism for adding
a PropertySource to Spring's Environment. To be used in conjunction
with #Configuration classes.
As per documentation above, to use #PropertySource to load the custom source, one has to use the #Configuration annotation.
#Configuration
#PropertySource("classpath:typesofcharge.properties")
Under the hood a #Configuration class is a #Component.
#Target(value=TYPE)
#Retention(value=RUNTIME)
#Documented
#Component
public #interface Configuration
So to your question. By specifying a custom #PropertySource without #Configuration, spring did not load the properties in the #PropertySource annotation and defaulted back to the application.properties.
If we use #PropertySource we have to use component otherwise the properties will not be read
since we added the #ComponentScan We don't have to mention #EnableConfiguationProperties annotation at all The propety class object can be autowired as Bean

trigger component scan from a application for an included spring boot jar using extra annotations

I need to publish a Spring boot based jar which should be consumed in other Spring/Spring boot based applications.
In my reuse jar I have a class(BusinessConfig) annotated with #Configuration and it gives out two beans. This class is in the base package of the reuse jar.
#Configuration
public class BusinessConfig {
#Bean(name = "BusinessRepoManager")
public BusinessRepoManager businessRepoManager(){
return BusinessRepoManager.getInstance();
}
#Autowired
#Bean(name = "CustomerManager")
#Scope("request")
public CustomerManager customerManager(BusinessRepoManager busrepoManager){
return CustomerManager.getInstance();
}
}
In the second application, I have added the dependency and in the application class I have the statement
#ComponentScan(basePackageClasses = {BusinessConfig.class})
to inform Spring context to look for beans provided in BusinessConfig class as well.
This works well, as I could see the beans getting created.
Is there any possibility to simplify this, should all consuming applications know the class name in which my configuration exists/package name.
I tried creating a custom annotation in the jar project and used that in the consuming application.
#ComponentScan(basePackageClasses = {BusinessConfig.class})
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Component
public #interface EnableDemoBusiness {
}
Then in my consuming application I just added
#EnableDemoBusiness
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Is there any way to get this work ?
Thanks in advance !
You have a couple of options:
Option 1
You can turn your class into "auto-configuration", by creating a META-INF/spring.factories file in your jar with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.your.package.BusinessConfig
Now in applications using your jar if #EnableAutoConfiguration or #SpringBootApplication annotations are used, your configuration will be processed and the beans created.
You might want to annotate your configuration with some #ConditionalXXX annotations if required to give applications that use your jar more control.
Refer to the documentation for more information.
Options 2
You can create a custom #EnableXXX annotation like you attempted.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Import(com.your.package.BusinessConfig.class)
public #interface EnableDemoBusiness {
}

spring boot application context was not properly loaded if the yaml file wasn't application.yml

With following configuration, my test can read the properties from the yaml file correctly.
#SpringBootApplication
#PropertySource("classpath:application.yml")
#ComponentScan({ "com.my.service" })
public class MyApplication {
}
Then I renamed the yaml file to my-application.yml, and changed the PropertySource to
#PropertySource("classpath:my-application.yml")
Tests are failed due to the null property value. The configuration class is as following:
#Configuration
#ConfigurationProperties(prefix="my")
#Data
public class MyConfig {
private String attr1;
}
The test class is:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
public class MyConfigTest {
#Autowired
private MyConfig myConfig;
#Test
public void getMyConfigTest() {
Assert.assertNotNull(myConfig.getAttr1());
}
Why spring boot can find the renamed yaml file, but it couldn't load the value correctly?
YAML files can’t be loaded via the #PropertySource annotation
It appears to work with #PropertySource("classpath:application.yml") because that's the default location and spring boot looks there regardless.
You may be able to use #ConfigurationProperties(location="claspath:my-application.yml") instead but it doesn't really achieve the same purpose (and I've never tried it myself).

Resources