Spring and #Value annotation - java

I'm trying to build a console application with Spring framework.
I have a class annotated by #SpringBootApplication:
#SpringBootApplication
public class LoaderApplication {
public static void main(String[] args) {
SpringApplication.run(LoaderApplication.class, args);
}
}
And class annotated by #Component
#Component
public class ElasticLoader implements CommandLineRunner {
// Elastic properties
#Value("${elasticTransportAddress:#{null}}")
private String elasticTransportAddress;
private static final Logger log = LoggerFactory.getLogger(ElasticLoader.class);
#Override
public void run(String... arg) throws Exception {
if (!( elasticTransportAddress != null && elasticTransportAddress.isEmpty() )) {
log.error("[elasticTransportAddress] is not defined in property file");
return;
}
log.info("-> ElasticLoader.run");
}
}
As you can see, at this class I’m trying inject property elasticTransportAddress value by #Value annotation, but after running my application I can see that property elasticTransportAddress stays unassigned.
What did I miss?
Let me to add some notes:
I want to use my application with different property files. For this case I've created xml-context-config.xml with such content:
<beans profile="companies">
<!-- allows for ${} replacement in the spring xml configuration from the
application-default.properties, application-dev files on the classpath -->
<context:property-placeholder
location="classpath:companies.properties"
ignore-unresolvable="true" />
<!-- scans for annotated classes in the com.env.dev package -->
<context:component-scan base-package="ru.alexeyzhulin" />
</beans>
and use configuration file companies.properties which I placed in
My run configuration with the described profile:
And this profile is enabled, as I could see in the application logs:
2017-01-15 00:10:39.697 INFO 10120 --- [ main] ru.alexeyzhulin.LoaderApplication : The following profiles are active: companies
But when I define properties in application.properties and use default profile, the property elasticTransportAddress becomes assigned.

rename companies.properties to application-companies.properties. Link to documentation

You have an error in the if condition.
This:
if (!( elasticTransportAddress != null && elasticTransportAddress.isEmpty() ))
is equivalent to this:
if (elasticTransportAddress == null || !elasticTransportAddress.isEmpty())
Thus, you get the error log message + return in TWO cases:
If the property is not defined OR
If the property is defined and it's value is non-empty.
The fix is to rewrite the if condition, e.g.
if (elasticTransportAddress == null || elasticTransportAddress.isEmpty())
Update 1: You of course need to make sure this value is really defined in application properties.
Update 2: Your xml-context-config.xml file isn't used by Spring at all.
The recommended way is to use annotation-based configuration with #SpringBootApplication. You can do this by deleting xml-context-config.xml completely and creating a class annotated with #Configuration. This configuration-class can include annotations which define property sources. E.g.:
#Configuration
#Profile("companies")
#PropertySource("classpath:companies.properties")
public final class LoaderApplication {
}
Note that you don't need to explicitly enable #ComponentScan if you use #SpringBootApplication.

As the other #Alex said, you have to tell Spring from where you want to retrieve those properties. What you are missing here is another configuration and in your case (Annotation based configuration) you have to add this class:
#Configuration
public class MyConfiguration {
#Bean
public PropertyPlaceholderConfigurer getCompanyConfigurer() throws IOException {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
configurer.setLocations(new ClassPathResource("classpath:companies.properties"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}
}
Update:
If you are using the XML based configuration you have to tell Spring Boot what is the XML configuration deleting your annotation #SpringBootApplication in LoaderApplication and adding the XML to SpringApplication in this way:
SpringApplication.run("classpath:application-configuration.x‌​ml", args);
Finally add this line to the xml:
<context:property-placeholder ignore-unresolvable="true" location="classpath:companies.properties"/>
And, that's it, hope it helped.

Related

Get value from application.properties without Main application

I' m working in a Spring Boot project, consisted in several applications booted with embedded Tomcat and a unique application used only as library for running applications. In that library application I need to retrieve a value from application.properties file for a variable. Being it a library application, it has not a Main class, so the variable will be always null.
My try was these:
public class AuthAdapter implements IAuthAdapter {
#Value("${signin.key}")
private String key;
#Override
public String getSigningKey() {
return key;
}
}
When the getSigninKey() is invoked by an application that uses it, the variable key is null.
How can I do to fill the variable key with the value present on appliaction.properties file, considering the situation explained before?
You need to annotate your AuthAdapter class with #Service/#Component/#Repository/#Configuration based on your requirement. By doing so, spring boot will inject the application.properties to your class during the spring context initialization.
#Value Spring annotation
This annotation can be used for injecting values into fields in Spring-managed beans
So make AuthAdapter as spring bean in some config class
#Configuration
public class ApplicationConfig {
#Bean
public AuthAdapter getAuthAdapter() {
return new AuthAdapter():
}
}
with property in application.yml or application.properties
application.yml
signin:
key: value
Or if AuthAdapter is annotated with any stereotype annotations here, Just enable that class in ComponentScan
#SpringBootApplication
#ComponentScan({ "com.project1", "com.library.AuthAdapter" })
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
If the variable key==null, this means that Spring did not inject it.
This could come from sevreral reasons:
the property "signin.key" is not present in the application.properties
SpringBoot did not start => make sure that in your application there is a main annoted with #springbootapplication
The AuthAdapter is not instanciate by Spring:
=> The AuthAdapter shall not be instanciate like:
authAdapter = new AuthAdapter();
Instead, it must be instanciate and injected by Spring itself like:
#Autowired
private authAdapter AuthAdapter;
For that the AuthAdapter must be annoted with a springBoot annotation:
#Service/#Component/#Repository/#Configuration.
Finally, the package containing the AuthAdapter class must be in the component scan perimeter to tell Spring to instanciate it.
Make sure that the "signin.key" is present in the application.properties and that the is present in the jar generated

Spring boot how to pick externalized spring properties file

I have this configurations which needs to be used for a spring boot application.
server.port=8085
server.servlet.context-path=/authserver
#data source
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=<url>
spring.datasource.username=<username>
spring.datasource.password=<password>
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
By default spring-boot picks up up the application.properties file located in src/main/resources/
I want to alter this path and direct spring boot to different application.properties file
I can achieve this using
java -jar app.jar --spring.config.location=classpath:/another-location.properties
Is there any any alternative solution I can achieve this without passing args through command line?
I was using this
#PropertySource("file:C:\Users\test\.test\test.properties")
#ConfigurationProperties(prefix = "spring")
public class Configuration {
private String ddlAuto;
private String url;
private String username;
private String password;
private String driverClassName;
}
in my Main class
#SpringBootApplication
#EnableConfigurationProperties(Configuration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
There after I tried executing the app commenting out all datasource properties in application.properties under src/main/resources/
But it keeps giving me the error mentioned bellow and application fails to start
I was referring this tutorial : https://www.mkyong.com/spring-boot/spring-boot-configurationproperties-example/
but as it's mentioned I get this error when i start the spring boot application
***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException:
Any help on this would be appreciated
The recommended way to have externalized properties is to use the spring.config.location system property, by starting your application like so:
java -jar -Dspring.config.location=/path/to/my/file.properties app.jar
The reason for this is that you don't add coupling between your code and your filesystem hierarchy.
Before Spring Boot 2.0 this property is additive, meaning that it will complement the default locations. After Spring Boot 2.0, spring.config.location replaces the default locations (e.g. classpath src/main/resources/application.properties). To keep the additive behaviour after 2.0, use spring.config.additional-location instead.
Please see here for official documentation on this matter.
I am able to make it work properly on Spring Boot 2.1.2.RELEASE. This is what I have done:
I have a test.properties in my /tmp folder with the following content:
test.myprop=hello
I also have the usual property file in the resources folder:
myprop=world
I have created a class for the custom property file:
#Configuration
#PropertySource("file:/tmp/test.properties")
#ConfigurationProperties(prefix = "test")
public class TestConfig {
private String myprop;
public String getMyprop() {
return myprop;
}
public void setMyprop(String myprop) {
this.myprop = myprop;
}
}
And then in my main class I have enabled to configuration properties:
#EnableConfigurationProperties(TestConfig.class)
#SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Now I have this test controller:
#RestController
public class TestController {
#Value("${test.myprop}")
private String externalisedProp;
#Value("${myprop}")
private String prop;
#GetMapping("test")
public void test() {
System.out.println("externalised: " + externalisedProp);
System.out.println("not externalised" + prop);
}
}
Which, once called, is properly printing:
externalised: hello
not externalised: world
My TestConfig class is in the same package as the MyApp main class.
What I have done is very similar, almost identical, to your solution, are you sure your path is correct? Also, I can see that the content of your property file is not matching what you have in your config class, the prefix is different. Maybe that is the problem?
Edit:
I have tried to remove the #Configuration annotation from my property class (which you do not have as well) and it is not able to pick up the externalised properties anymore. The error is different though but you should try to add it.

Spring - Inject Dependency based on properties

Does Spring Boot have a way to inject a dependency with the class name and constructor properties provided in the config file?
For example, I have two version of a common interface, IFileStore, FileStoreA and FileStoreB. I want to be able to define which of these I should use in the application.yml file.
I know I can do something like this:
#Value("${fileStore.class}")
private String fileStoreClassName;
#Bean
public IFileStore fileStore() {
switch(fileStoreClassName) {
case "FileStoreA":
return new FileStoreA();
case "FileStoreB":
return new FileStoreB();
}
}
This however feels really hacky. I'd also have to manually extract and supply any required parameters to them.
My ideal would be that it's able to determine which to use based on the class name, and also provide any parameters the specific one needs, so if I add a third FileStore, it'd auto-magically work and I'd just have to use that for the class name instead.
If you really only need a single bean, then create a conditional configuration
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreA")
public class FileStoreAConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreA(...);
}
}
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreB")
public class FileStoreBConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreB(...);
}
}
It's actually easier than that, as the annotation can be used on a method instead, rather than having separate configuration classes.
See the ConditionalOnProperty Javadoc
You can use Spring Profiles (#Profile annotation) in order to configure the same #Bean but with different implementations.
For example, you can make a production configuration like this:
#Configuration
#Profile("production")
public class ProductionConfiguration {
// ...
}
So, for your example, you can configure how many profiles you require and then you can specify the property in any of the usual ways, for example, you could include it in your application.properties.
For further details, you can read Spring Boot features - Profiles
Are you perhaps looking for XML-based configuration?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="fileStore" class="com.example.FileStoreA">
<property name="parameter1" value="Hello World!"/>
</bean>
</beans>

How to load a properties file based on the server environment with spring so that the values can be injected?

To my surprise I have had a difficult time finding an answer to this question. I have Seen many examples where you can use #PropertySource to load a specific properties file for a class. I have also seen examples where you can easily add different property files in spring boot projects. But what I want to do is to do this for a spring project that is NOT spring boot and load a properties file so that the values of this file can be injected in classes annotated with #Component which is dependent on the server environment. So for example if I am on development server I want a particular properties file loaded and on production a different properties file. The reason that I am doing it like this is because my data and service layers are their own modules. These modules contain their own unit tests and can be imported as their own modules in other spring boot projects. I need properties files to be loaded to serve these modules which use spring but not spring boot. I have tried the following, but this does not work.
#Configuration
#Profile("test")
#EnableJpaRepositories("com.hi.repository")
#EnableTransactionManagement
#EnableScheduling
public class InfrastructureConfig {
...
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Map<String, String> env = System.getenv();
String propertiesFile=null;
String e = env.get("SERVER_ENV");
if (e.equals("dev")) {
propertiesFile = "environment/development.properties";
} else if (e.equals("prod")) {
propertiesFile = "environment/production.properties";
}
configurer.setLocation(new ClassPathResource(propertiesFile));
return configurer;
}
Then I have a test which looks like this
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/DealServiceTest-context.xml"})
#ActiveProfiles("test")
public class LogTest {
private static final Logger log = LogManager.getLogger(LogTest.class);
#Autowired
PathsService pathsService;
#Autowired
Environment environment;
#Test
public void testBeans(){
System.out.println("********** WASSUP from LogTest");
System.out.println(environment.getProperty("imageBucket"));
}
Although the test prints out null which indicates to me the properties file has not been loaded and prepared for its values to be injected. How can I achieve this?
You don't really need to set properties yourself, but you can do this using spring configuration. Check the documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
If you're using spring boot - all you need to do is create multiple properties file for your environments. And only for properties you need to override.
So your main properties file would be at
src/main/resources/application.properties
Production
src/main/resources/application-prod.properties
Development
src/main/resources/application-dev.properties
Testing
src/main/resources/application-test.properties
And then just use the profile name as your environment variable
java -jar -Dspring.profiles.active=prod demo-0.0.1-SNAPSHOT.jar
Actually, you can just use a placeholder in #PropertySource annotation.
See documentation:
Any ${...} placeholders present in a #PropertySource resource location will be resolved against the set of property sources already registered against the environment.
Assuming that placeholder is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value.
I've made a simple example, it receives a 'property.environment' value to choose, which .properties file should be used as property source. I have two resource files in my classpath - application-test.properties and application-dev.properties, each one contains a 'test.property' value ('test-env' and 'dev-env' respectively).
Property configuration:
#Configuration
#PropertySource("classpath:/config/application-${property.environment}.properties")
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
return propertySourcesPlaceholderConfigurer;
}
}
Component with #Value
#Component
public class TestService {
#Value("${test.property}")
String testProperty;
#PostConstruct
void init() {
System.out.println("---------------------------------------------------------");
System.out.println("Running in " + testProperty + " environment");
System.out.println("---------------------------------------------------------");
}
}
Build command line example (it runs tests with test environment properties)
mvn clean install -DargLine="-Dproperty.environment=test"
Output
---------------------------------------------------------
Running in test-env environment
---------------------------------------------------------
Run command line example
java -jar -Dproperty.environment=dev PATH_TO_YOUR_JAR.jar
Output
---------------------------------------------------------
Running in dev-env environment
---------------------------------------------------------
Don't hard code based on different environment, in spring boot you can able to maintain properties specific environment easily. Refer https://spapas.github.io/2016/03/31/spring-boot-settings/
I would try to take advantage of the profile mechanism already in place in Spring. You basically have done the job yourself already, the only thing you need to change is to have different configurations for "test" and "production" profiles. I prefer to keep everything related to test away from production code (allowing me to place the TestConfig class below in the test source path), so I would probably do something like this:
#Configuration
#Profile("!test")
#PropertySource(value = "classpath:/environment/production.properties")
#Import(AppConfig.class)
public class ProductionConfig
{
// Your production-specific config goes here
}
#Configuration
#Profile("test")
#PropertySource(value = "classpath:/environment/development.properties")
#Import(AppConfig.class)
public class TestConfig
{
// Your test-specific config goes here
}
#Configuration
public class AppConfig
{
// Needed for spring to handle ${property:default} syntax
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If you prefer to have one config for both cases, you can let the AppConfig import the TestConfig and the ProductionConfig instead, but that will put test code in to production...
Good luck with your project!

Property from file is not found with Spring

I am running into a problem where I can't access a Spring property from my Java code.
Here is the context:
<context:property-placeholder ignore-resource-not-found="false"
location="file:/${setup.properties.directory}/setup.properties"/>
The setup.properties file looks like this:
paymentProvider.x.url=x
The code is:
SpringContext.INSTANCE.getEnvironment()
.getProperty("paymentProvider.x.url");
There are no errors during run. However, the result of the code above gives null.
Anyone knows why?
Modify context:property-placeholder configuration like:
<context:property-placeholder location="classpath:setup.properties" />
Where you want to access this properties value on that class level use one annotation like:
#PropertySource(value = { "classpath:setup.properties" })
And to read the properties values autowire Environment inside that class like:
#Autowired
private Environment environment;
And finally you can access like:
environment.getRequiredProperty("paymentProvider.x.url");
Only
#Configuration
#PropertySource("file:/${setup.properties.directory}/setup.properties")
public class SpringContext {
public static final ApplicationContext PROPERTIES_INSTANCE = new AnnotationConfigApplicationContext
(SpringContext.class);
}
works for me.

Categories

Resources