How to load #Value annotation in the main class? - java

#SpringBootApplication
public class App {
#Value("${Application}")
private String application;
#Value("${APP_SERVER_CPU_ENABLED}")
private String APP_SERVER_CPU_ENABLED;
#Value("${APP_SERVER_MEMORY_ENABLED}")
private String APP_SERVER_MEMORY_ENABLED;
#Autowired
MonitoringItems mI;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
MonitoringItems mI=null;
try {
System.out.println(APP_SERVER_CPU_ENABLED);
System.out.println(APP_SERVER_MEMORY_ENABLED);
if (APP_SERVER_MEMORY_ENABLED.equalsIgnoreCase("true") && APP_SERVER_CPU_ENABLED.equalsIgnoreCase("true")) {
//do something
}
How come the main class cannot read the #Value annotations? In my other classes I have to put #Configuration, #ComponentScan, #EnableAutoConfiguration above the class and then #PostConstruct above the constructor. And I was able to retrieve the values from the application.properties. How would I be able to do that for the main class?

The problem is that you cannot make your #Values static, because Spring may not have initialized them yet. You can however get the value you want BEFORE spring is initialized by retrieving them from the system/environment variables instead of via a properties file.
String value = System.getProperty("SOME_KEY");

So after 3 years i show you an astuce to do that.
#SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Demo1Application .class, args);
Test test = context.getBean(Test.class);
String password = test.getPassword();
System.out.println(password);
}
#Component
class Test {
#Value("${password}")
private String password;
public String getPassword() {
return password;
}
}
}
of course you create your property in a file properties like application.properties for example :
password = sxqdqdqdqdqdqd

Related

Spring : Cannot get value from application.properties

I created Spring project via Spring Initializr with project following struct:
I defined property in application.properties file :
my.prop=testvalue
I inject this value into MyClass as following :
#Component
class MyClass {
#Value("${my.prop}")
private String myProp;
public String getMyProp() {
return myProp;
}
}
ConfigBeans defined as following:
package com.example.propertiesdemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ConfigBeans {
#Bean
public MyClass myLitoBean() {
return new MyClass();
}
}
PropertiesdemoApplication.java :
package com.example.propertiesdemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
ConfigBeans.class);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am expecting that after executing line
System.out.println(myClass.getMyProp());
will be printed value of myprop defined in application.properties (i.e testvalue), but after running (via Netbeans IDE) I get output :
${my.prop}
What was missed / wromg in this code ? Thanks in advance
You are creating MyClass bean twice.
Using #component annotation
using #bean annotation in the config class (use method name lowerCamelCase i.e. in your case myClass())
Create bean only once using any one of the above.
You dont need to create an application context in the main method like this. The presented code is a kind of mixture of "traditional" spring and spring boot. So you're kind of bypassing all the goodies that spring boot offers, among which is automatic application.properties loading.
If you're using spring boot (there is a #SpringBootApplication annotation) then it will create everything by itself.
Usually it should be something like this
public static void main(String[] args) {
SpringApplication.run(PropertiesdemoApplication.class, args);
}
Right, as Navnath Adsul said, you need the bean to be created once, and also, since you are using Spring Boot, you need to raise the context using a special method
#SpringBootApplication
public class PropertiesdemoApplication implements CommandLineRunner {
// Inject Bean
#Autowired
private MyClass myClass;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(PropertiesdemoApplication.class)
.run(args);
// or SpringApplication.run(PropertiesdemoApplication.class, args);
}
#Override
public void run(String[] args) throws Exception {
System.out.println(myClass.getMyProp());
}
}
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PropertiesdemoApplication.class, args);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}

BeanCreationException error when referencing configuration class inside a service class

I am trying to #Autowire a #Configuration class inside a #Service class. basically my #Configuration class contains mapping to my custom .properties file. When i try to autowire my configuration class inside my service class, BeanCreationException occurs. I am not sure what happen. Just followed the guide on creating Property classes from spring. There must be something i missed out.
Also, when i try to autowire #Configuration class to another #Configuration class, it runs smoothly
Currently, i know that, prop is always null because when i remove prop.getUploadFileLocation() call, everything will be fine. There must be something wrong during autowiring.
Here is my Service class
#Service
public class ImageService {
public static Logger logger = Logger.getLogger(ImageService.class.getName());
#Autowired
MyProperties prop;
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
Here is my Configuration class
#Data
#Configuration
#ConfigurationProperties (prefix = "my")
public class MyProperties {
private String resourceLocation;
private String resourceUrl;
public String getUploadFileLocation() {
return getResourceLocation().replace("file:///", "");
}
public String getBaseResourceUrl() {
return getResourceUrl().replace("**", "");
}
}
And here is where i can successfully use MyProperties
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
#Autowired
MyProperties prop;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(prop.getResourceUrl())
.addResourceLocations(prop.getResourceLocation());
}
}
The issue is that you are trying to use an autowired field to set the value in an inline field assignment.
That means
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
is executed before the prop is autowired, meaning it will always be null
The way to mitigate this would be to use constructor injection instead.
#Service
public class ImageService {
//Fine since you are using static method
public static Logger logger = Logger.getLogger(ImageService.class.getName());
//Not needed if you are only using it to set FILE_UPLOAD_LOCATION
//Allows field to be final
private final MyProperties prop;
//Still final
private final String FILE_UPLOAD_LOCATION;
//No need for #Autowired since implicit on component constructors
ImageService(MyProperties prop){
//Again not needed if you aren't going to use anywhere else in the class
this.prop = prop;
FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
}
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
See this question for why constructor is preferred over #autowired in general
If you need MyProperties bean to be created before StaticResourceConfiguration bean, you can put #ConditionalOnBean(MyProperties.class) as following. Spring will make sure MyProperties is there before processing StaticResourceConfiguration.
#Configuration
#ConditionalOnBean(MyProperties.class)
public class StaticResourceConfiguration implements WebMvcConfigurer {

Load properties when MockitoJUnitRunner is used

I'm trying to load a property value from my application-test.properties in my service unit tests(using MockitoJUnitRunner.class) but it gets always a null value using #Value annotation.
#RunWith(MockitoJUnitRunner.class)
public class AlertQueryServiceImplTest {
#Value("${raw.path}")
private static String rawJson;
#InjectMocks
private AlertQueryServiceImpl alertQueryServiceImpl;
#Mock
private AlertCBDAOImpl alertCBDAOImpl;
private List<Alert> alertListExpected;
#Before
public void setUp() {
ReflectionTestUtils.setField(alertQueryServiceImpl, "url", "http://someurl");
alertListExpected = JsonUtils.
loadObjectList(Alert.class, JsonUtils.ALERTS_FILE);
assertFalse(alertListExpected.isEmpty());
}
#Test
public void shouldReturnAlerts() {
User userTest = new User("108998", "3000747091");
JsonDocument jsonDocExpected = JsonUtils.stringToJsonDocument(rawJson, userTest.getAssociatedBE());
when(alertCBDAOImpl.getDoc(userTest.getAssociatedBE())).thenReturn(jsonDocExpected);
when(alertCBDAOImpl.getAlerts(jsonDocExpected)).thenReturn(alertListExpected);
}
}
How could I load raw.path property into rawJson field?
First you need to set active profile to access application-test.properties file you may do that using #ActiveProfiles("test") annotation on your test class.
https://www.baeldung.com/spring-testing-separate-data-source
I don't think #Value annotation will work with static keyword.

Load Config File Issue in Spring Boot

I am trying get values for string Constants from config.properties file in Spring boot application. But when i am trying to access those values from java classes and coming as null.
Please find the code as below:
Config.properties file:
## user Details##
user.username=xyz
user.password=123456
##customer Details ##
customer.username=abc
customer.password=123456
ConfigProp class:
#Component
#PropertySource("classpath:config.properties")
public class ConfigProp {
#Value("${user.username}")
private String userName;
#Value("${user.password}")
private String password;
#Value("${customer.username}")
private String custUserName;
#Value("${customer.password}")
private String custPassowrd;
}
Test Class:
public class Test {
public static void main(String args[]) {
ConfigProp configProp = new ConfigProp();
System.out.println(configProp.getUserName());//coming as null
}
}
SpringBoot App main class:
#SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
I am not getting the issue exactly , why values are not coming. Can anyone please help me on this and let me know in case of any other details required.
First problem here:
ConfigProp configProp = new ConfigProp();
You are not letting spring maintain this dependency and hence its not recognizing this as bean.
You Testclass can look like this:
public class Test {
#Autowired
ConfigProp configProp;
public static void main(String args[]) {
System.out.println(configProp.getUserName());//coming as null
}
}
The second problem here is presence of two main methods. You should write your Test class as Junit test which would look something like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test {
#Autowired
ConfigProp configProp;
#Test
public void contextLoads() {
assertEquals("abc", configProp.getCustUserName());
}
}
Also Test class should be located in src/test/java location.

Providing context for #Value fields from a unit test

I have a class like this:
#Service("someClient")
public class SomeClient {
#Value{some.value}
private String someValue;
public void someMethod() {
return someValue;
}
}
And a test like this:
#ContextConfiguration(locations = "classpath:/some/where/testApplicationContext.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
#Value{some.value}
private String someValueTest;
#Test
public void shouldWork() {
...
someClient.someMethod()
...
}
}
When the wider application is running, the field someValue inside the SomeClient class is populated from a properties file referenced from testApplicationContext.xml. When I run the test in debug mode I can see that someValueTest is populated in the test, but when the test calls the class under test, the value is not populated.
I could use some advice! Obviously I can change the visibility of the field in the class, or provide a setter, however I would like to avoid that if possible. If it isn't, please advise.
In order to populate fields with #Value annotation in your test you need to configure PropertySourcesPlaceholderConfigurer.
Add the following to your test:
#Configuration
public static class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
To read the values from test property file you can add
#TestPropertySource(locations="classpath:test.properties") to your Test class declaration
You can use ReflectionTestUtils from org.springframework.test.util.ReflectionTestUtils package to mock any variable, including the ones that access the properties file.
#RunWith(SpringJUnit4ClassRunner.class)
public class SomeClientTest extends TestCase {
private SomeClient someClient;
#Test
public void shouldWork() {
//Initialize someClient
someClient = new SomeClient();
ReflectionTestUtils.setField(someClient, "variable name", "the variable value");
someClient.someMethod()
...
}
}

Categories