Overriding Spring #PropertySource by #Import - spring

I have a property test=default in class DefaultConfig, and I'm making them available using #PropertySource annotation.
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
I then want to be able to override to test=override, which is in a different properties file in class OverrideConfig, so I again use #PropertySource.
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {}
I configure a test to prove that it works.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={OverrideConfig.class})
public class TestPropertyOverride {
#Autowired
private Environment env;
#Test
public void propertyIsOverridden() {
assertEquals("override", env.getProperty("test"));
}
}
Except of course it does not.
org.junit.ComparisonFailure: expected:<[override]> but was:<[default]>
Maxing out debug, I can see what's happening:
StandardEnvironment:107 - Adding [class path resource [default.properties]] PropertySource with lowest search precedence
StandardEnvironment:107 - Adding [class path resource [override.properties]] PropertySource with lowest search precedence
It seems backwards. Am I making a simple mistake or misthinking this, or would you expect the properties defined by an #PropertySource in an #Import-ed configuration class to be overridden by properties defined in am #PropertySource in the #Import-ing class?

Here is a solution by Helder Sousa, written as a comment of the JIRA issue created by the OP:
[T]he behaviour available in spring xml (one xml importing another xml) is achievable using nested configurations:
#Configuration
#PropertySource("classpath:default.properties")
public class DefaultConfig {}
#Configuration
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Configuration
#Import(DefaultConfig.class)
static class InnerConfiguration {}
}
With this setup, the properties will be gather in the proper order.

Today with Spring 4 you can use this:
#TestPropertySource(value="classpath:/config/test.properties")
And this can be used to use and eventually override properties for the junit test:
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(value="classpath:/config/test.properties")

I'm currently struggling with a similar case in Spring 3.1 but I'm using a different approach to override properties, because #PropertySource does not support optional property files:
#Configuration
#PropertySource("classpath:default.properties")
public class BaseConfig {
#Inject
private ApplicationContext context;
#PostConstruct
public void init() throws IOException {
Resource runtimeProps = context.getResource("classpath:override.properties");
if (runtimeProps.exists()) {
MutablePropertySources sources = ((ConfigurableApplicationContext) context).getEnvironment().getPropertySources();
sources.addFirst(new ResourcePropertySource(runtimeProps));
}
}
...
It seems that #Import does not cause any specific order of #Configuration instantiation whatsoever besides the order dictated by normal bean dependencies. A way to force such an order is to inject the base #Configuration instance itself as a dependency. Could you try:
#Configuration
#Import(DefaultConfig.class)
#PropertySource("classpath:override.properties")
public class OverrideConfig {
#Inject
private DefaultConfig defaultConfig;
...
}
Does this help?
And maybe the new ContextHierarchy annotation could be of help here also but I didn't try this one out so far.

You could enforce the loading order of your properties like this:
#Configuration
#PropertySource(value={"classpath:default.properties","classpath:override.properties"})
public class OverrideConfig {
...
}

I had a similar problem and succeeded in just declaring also the default property in my custom configuration:
#Configuration
#Import(DefaultConfig.class)
#PropertySource({"classpath:default.properties", "classpath:override.properties"})
public class OverrideConfig {}

Related

Should #ComponentScan stay in the class containing the main method?

I know that #ComponentScan with #Configuration tell Spring where to look for beans.
#ComponentScan
#Configuration
public class MyApp{
...
}
What I do not understand is on which class I have to put these two annotations. Should they stay on the class containing the main method?
Like this
#ComponentScan
#Configuration
public class MyApp{
public static void main(String[] args) {
...
}
}
Or they can stay on whatever class of the application?
The question comes from the fact that Spring has to know the location of #ComponentScan... or is there an automatic way of detection of the #ComponentScan annotation which Spring is performing under the hood?
Hope to have explained myself!
You can put it wherever you want (I usually put mine in com.domain.project-name.config) and just specify the directories it should scan, for example if you want it to scan everything in project use
#ComponentScan("com.domain.project-name")
#Configuration
public class Config {
...
By default, ComponentScan scans all the annotated classes at the current directory level and below.
#Configuration annotation tells the Spring container that the class contains Spring bean configuration.
#ComponentScan annotation tells the Spring container that the annotated class to scan/searches for other annotations and components. You can also define package name to scan with the annotation like #ComponentScan("your.package.name") or you can give package/class names that need not be scanned.
Hence, you can put these annotations on any class that defines your bean configuration and could be required by spring container to parse and create objects for your entities/POJOs, services and DAOs.
To conclude, I would like to add #ComponentScan and other annotations are there for automatic detection. Else, you would need to define XMLs (that's what happens under the hood with annotations) for spring to read and perform these actions.
Using simple example. You can place #ComponentScan with #Configuration in any class which main method can scan.
Main class scans MyScan class which then scan for bean class.
package com.boot.spring;
#SpringBootApplication
#ComponentScan(basePackages = "com.boot.scan")
public class BootApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(BootApplication.class, args);
System.out.println(ctx.getBean("demoBean"));
}
}
Bean class is in different package
package com.boot.bean;
#Service
public class DemoBean {
}
Now, bean class is discovered through DemoScan class
package com.boot.scan;
#ComponentScan(basePackages = "com.boot.bean")
#Configuration
public class DemoScan {
}

Spring Java Config: configure already existing bean

I want to configure in my #Configuration class bean which is already created by other library's autoconfiguration. I just need to change some fields in that class after it's being initialized.
But I can't find a proper way how to provide code block in #Configuration class and not using #Bean annotation. Is there an ideomatic way to do so in spring?
One way to do this:
#Configuration
class TestConfig {
#Autowired
private SomeBean someBean;
#PostConstruct
private void initSomeBean() {
// someBean.setProperty("qwe");
}
}
#PostConstruct annotation defines init-method, which is getting called after SomeBean is autowired. In this method you can adjust your bean
Do you want to import one Config in AnotherConfig on? It can be done via annotation placed on AnotherConfig:
import org.springframework.context.annotation.Import;
...
#Import(value = {Config.class})
public class AnotherConfig ... {}

#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

Spring automagic, #Autowired

#Autowired
Environment env;
#Value("${jdbcConnectionString}")
private String jdbcConnectionString;
The above works automagically in certain classes, however is null in similar classes in same package and with same annotations #Configuration/#Component.
I am trying to find out the proper approach to understand what spring does when while configuring various artifacts.
I am able to run stuff every now and then but any good resource to understand the magic is well appreciated.
PS. I am only interested in java-config based approach now.
Working :
package a.b.c;
#Configuration
public class AppConfig {
#Autowired
Environment env;
package a.b.d;
#Configuration
#EnableBatchProcessing
public class JobConfiguration {
#Autowired
private Environment env;
package a.b.L;
public class BatchJobListener implements Ordered, JobExecutionListener {
#Autowired
public Environment env;
Not working inside
package a.b.u
Tried to annotate classes with #Component/#Configuration
In order to autowire a bean, you need first to defined it in a Context.
#Configuration
public class ConfigOne {
#Bean
public String myBean(){
return "my bean";
}
}
The bean that you want to inject and the bean where the bean will be injected need to be in the same context. You can do it with:
JavaConfigApplicationContext context =
new JavaConfigApplicationContext(ConfigOne.class, ConfigTwo.class);
Or you can use #import to import one configuration class into another.
#Configuration
#Import(ConfigTwo.class)
public class ConfigOne {
UPDATE
What I meant was that probably you're not making the configuration in the ringht way. So all your beans where you're injecting the enviroment are not in the same context.
However, if you have configured well everything, it's possible that some classes are loading before enviroment. In this case you can use EnvironmentAware
#Configuration
#PropertySource("classpath:myProperties.properties")
public class MyConfiguration implements EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(final Environment environment) {
this.environment = environment;
}
public void myMethod() {
final String myPropertyValue = environment.getProperty("myProperty");
// ...
}
}
In your main class where you are initializing the spring boot application, do you have similar configuration:
#Configuration
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan("a.b") //Note that this scans the components where you have configured spring container backed objects
#PropertySource({
"classpath:someProperty1.properties",
"classpath:someProperty2.properties"
})
public class Main{
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
What this basically does is, it tells spring that this is a configuration class, and indicates this configuration class that it also triggers auto-configuration and component scanning (scan only these specific packages (a.b) and check if there are any annotations present for automatic bean detection, like: #Component, #Service, #Controller, #Repository). On detecting any class which have these stereotypes spring would create object(s) depending on the configuration. On creating these objects one may have autowired some objects or one could refer to some property defined in properties file. The config #PropertySource does this.
The package declaration in #ComponentScan should be the base package.

DRY Spring AnnotationConfig testing

So, I'm working on some Spring tests which require dependency injection using annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
static class ContextConfiguration {
#Bean
public SomeService someService() {
return new SomeService();
}
}
}
I'd really like to not have to repeat this code in every test but my attempts to create a base class which contains the configuration:
#Configuration
class MyContextConfiguration {
#Bean
public SomeService someService() {
return new SomeService();
}
}
And deriving from it:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
static class ContextConfiguration extends MyContextConfiguration {}
}
Don't seem to work. Can anybody suggest a way to DRY this up?
Thanks!
You should be able to do this instead.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class BeanTest {
#Autowired
private SomeService someService;
#Configuration
#Import(MyContextConfiguration.class)
static class ContextConfiguration {
....
}
}
Also, you don't need to mention AnnotationConfigContextLoader, Spring by convention will automatically pick up the static inner class annotated with #Configuration and use the appropriate ContextLoader
You can declare configuration classes in the the contextconfiguration-annotation. From the documentation.
ContextConfiguration
Defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically, #ContextConfiguration declares the application context resource locations or the annotated classes that will be used to load the context.
Resource locations are typically XML configuration files located in the classpath; whereas, annotated classes are typically #Configuration classes. However, resource locations can also refer to files in the file system, and annotated classes can be component classes, etc.
example from the documentation.
#ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}

Resources