Property values in bean class are null in constructor - java

I'm trying to use values from my application.properties file in my service implementation class/bean. But when the bean is initialized through my config class, the property values are all null.
Config class:
#Configuration
public class AppConfig {
#Bean AppServiceImpl appServiceImpl() {
return new AppServiceImpl();
}
}
Service class:
#Component
public class AppServiceImpl implements AppService {
#Value("${value.one}")
String value_one;
#Value("${value.two}")
String value_two;
#Value("${value.three}")
String value_three;
//values are null here
public AppServiceImpl() {
functionOne(value_one, value_two, value_three);
}
}
application.properties(under src/main/resources):
value.one=1
value.two=2
value.three=3
Doing some debugging i can see that the AppConfig class has found the properties file and if i try to declare #Value("${value.one}") String value_one; there it shows it has been given the value 1 as expected.
But in my AppServiceImpl class, all the values are null. What am I doing wrong here? How should this be done properly in Springboot? Or just Spring even.
Thanks.

If you use the values in the constructor they won't be available right away. Indeed they are injected on attribute. What's happening here is after spring created an instance then it will update the attribute value.
If you want to use those values in the constructor you should use constructor injection. Injection by constructor is a best practice.
public class AppServiceImpl implements AppService {
String value_one;
String value_two;
String value_three;
//values are null here
public AppServiceImpl(String value1, String value2, String value3) {
value_one = value1;
value_two = value2;
value_three = value3;
functionOne(value_one, value_two, value_three);
}
}
And your configuration class
#Configuration
public class AppConfig {
#Bean
AppServiceImpl appServiceImpl(#Value("${value.one}") String value1, #Value("${value.two}") String value2, #Value("${value.three}") String value3) {
return new AppServiceImpl(value1, value2, value3);
}
}

Those values are injected after the instance is created. So they are null in the constructor.
To execute a method after the values are injected use #javax.annotation.PostConstruct:
#PostConstruct
public void init(){ // method name doesn't matter
functionOne(value_one, value_two, value_three);
}

Related

#Value property is returning null in the spring component

I have created a validator component in spring boot and I am keeping a regex expression in the application.properties. I have used #Value annotation to get the value of regex in my component and I am compiling the Pattern outside any method or constructor and that is giving me null pointer exception as the regex is not getting it's value at that time. But when I move the pattern to some method, it's working fine. Why is that?
Why is #Value not working even though object is created using #Component
Look at the code below:
Code returning NullPointerException:
#Component
public class ValidString implements ConstraintValidator<ValidString, String> {
#Value("${user.input.regex}")
private String USER_INPUT_REGEX;
private Pattern USER_INPUT_PATTERN = Pattern.compile(USER_INPUT_REGEX);
#Override
public boolean validate(String userInput, ConstraintValidatorContext constraintValidatorContext) {
return USER_INPUT_PATTERN.matcher(userInput).find();
}
}
Code working fine:
#Component
public class ValidString implements ConstraintValidator<ValidString, String> {
#Value("${user.input.regex}")
private String USER_INPUT_REGEX;
private Pattern USER_INPUT_PATTERN;
#Override
public boolean validate(String userInput, ConstraintValidatorContext constraintValidatorContext) {
USER_INPUT_PATTERN = Pattern.compile(USER_INPUT_REGEX);
return USER_INPUT_PATTERN.matcher(userInput).find();
}
}
Also if you could explain why the first one is not working and second one is working, that'd be great.
application.properties
user.input.regex = ^[a-zA-Z0-9/\\-_ \\s+]*$
Field initializers (first example in question) are executed during the class constructor execution. #Value is injected by Spring after the constructor returns, using reflection. This means that you cannot have initializers using #Value-injected values.
The issue can be resolved by constructor or setter injection:
// Inject using constructor
#Component
public class ValidString implements ConstraintValidator<ValidString, String> {
private Pattern USER_INPUT_PATTERN;
#Autowired
public ValidString(#Value("${user.input.regex}") String regex) {
this.USER_INPUT_PATTERN = Pattern.compile(regex);
}
// Inject using setter method
#Component
public class ValidString implements ConstraintValidator<ValidString, String> {
private Pattern USER_INPUT_PATTERN;
#Autowired
private void setUserInputRegex(#Value("${user.input.regex}") String regex) {
this.USER_INPUT_PATTERN = Pattern.compile(regex);
}
Pattern USER_INPUT_PATTERN is before spring process.
the class ValidString object initial order is: ->process field initial->constructor -> inject field
so, when you use 1 code, it must be null point, because field has no inject

Spring Boot cache not caching method call based on dynamic controller parameter

I am attempting to use Spring Boot Cache with a Caffeine cacheManager.
I have injected a service class into a controller like this:
#RestController
#RequestMapping("property")
public class PropertyController {
private final PropertyService propertyService;
#Autowired
public PropertyController(PropertyService propertyService) {
this.propertyService = propertyService;
}
#PostMapping("get")
public Property getPropertyByName(#RequestParam("name") String name) {
return propertyService.get(name);
}
}
and the PropertyService looks like this:
#CacheConfig(cacheNames = "property")
#Service
public class PropertyServiceImpl implements PropertyService {
private final PropertyRepository propertyRepository;
#Autowired
public PropertyServiceImpl(PropertyRepository propertyRepository) {
this.propertyRepository = propertyRepository;
}
#Override
public Property get(#NonNull String name, #Nullable String entity, #Nullable Long entityId) {
System.out.println("inside: " + name);
return propertyRepository.findByNameAndEntityAndEntityId(name, entity, entityId);
}
#Cacheable
#Override
public Property get(#NonNull String name) {
return get(name, null, null);
}
}
Now, when I call the RestController get endpoint and supply a value for the name, every request ends up doing inside the method that should be getting cached.
However, if I call the controller get endpoint but pass a hardcoded String into the service class method, like this:
#PostMapping("get")
public Property getPropertyByName(#RequestParam("name") String name) {
return propertyService.get("hardcoded");
}
Then the method is only invoked the first time, but not on subsequent calls.
What's going on here? Why is it not caching the method call when I supply a value dynamically?
Here is some configuration:
#Configuration
public class CacheConfiguration {
#Bean
public CacheManager cacheManager() {
val caffeineCacheManager = new CaffeineCacheManager("property", "another");
caffeineCacheManager.setCaffeine(caffeineCacheBuilder());
return caffeineCacheManager;
}
public Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(200)
.maximumSize(500)
.weakKeys()
.recordStats();
}
}
2 solutions (they work for me):
remove .weakKeys()
propertyService.get(name.intern()) - wouldn't really do that, possibly a big cost
Sorry, but I don't have enough knowledge to explain this. Probably something to do with internal key representation by Caffeine.

Spring Boot - Assign value to Class Member from YML file - Null Pointer Exception

I am using below annotations in my config class to get the values from properties file(yml).
Configuration
EnableConfigurationProperties
ConfigurationProperties (prefix = "notification")
I am able to get the values inside public methods without problem using the class . But I am getting 'Error Creating bean' Error when I try to assign value instance variable of the class using config class.
Below is my code. Can someone please throw some light.
This is my config class
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties (prefix = "notification")
public class NotifyYaml {
private String subscriptionId;
public String getSubscriptionId() {
return subscriptionId;
}
public void setSubscriptionId(String subscriptionId) {
this.subscriptionId = subscriptionId;
}
Below is the class where I am getting error during startup.
#Component
public class PubSubController {
#Autowired
private NotifyYaml notify;
public PubSubController() {
// TODO Auto-generated constructor stub
}
String projectId = "ccc-g-pre-proj-cacdate";
//Error in this line
String subscriptionId = notify.getSubscriptionId();
The #Autowired object only gets filled in after the object is created.
This means that while the object is being created, it tries to call a method from a null object.
I would suggest using something like a #PostConstruct method. (Note: you will need to include javax.annotations into your project somehow.)
String subscriptions; // remove the value for now...
#PostConstruct
private void init() {
subscriptions = notify.getSubscriptionId(); // ...and add it back in here.
}

Read application.properties from non-component class like pojo or singleton

I'm using spring boot & for properties I've placed application.properties inside src/main/resources
From #Controllers & #Services, I'm able to read properties, but from my model pojo & a singleton class, I'm not able to read the values.
My #SpringBootApplication is in com.vehicle & I've not overriden #ComponentScan, so I belive it should read all the underlying packages.
Below is code:
application.properties
vehicle.lift.maxWeight=10
vehicle.lift.error.overWeight=Overweight
vehicle.battery.default=5.0
vehicle.battery.critical.limit=15
vehicle.walk.distance.error=Not able to move
WalkServiceImpl.java (able to read from here)
package com.vehicle.prototype.service.impl;
#Service
public class WalkServiceImpl implements CapabilityService {
#Value("${vehicle.walk.distance.error}")
private String mDistanceError;
#Override
public void performTask(VehicleData vehicleData) {
double distance = vehicleData.getWalkingDistance();
double remainingBattery = vehicleData.getRemainingBattery();
if (remainingBattery < distance) {
vehicleData.setErrorMessage(mDistanceError);
System.out.println(mDistanceError);
} else {
vehicleData.setRemainingBattery(remainingBattery - distance);
}
VehicleUtil.checkBatteryStatus(vehicleData);
}
}
VehicleData.java (Pojo - not able read from here)
package com.vehicle.prototype.model;
public class VehicleData {
private double walkingDistance;
private double liftWeight;
#Value("${vehicle.battery.default}")
private double remainingBattery;
// setters & getters ....
}
VehicleUtil.java (Singleton - not able to read from here)
package com.vehicle.prototype.utils;
public class VehicleUtil {
private static VehicleUtil mInstance = null;
private static Object mLock = new Object();
#Value("${vehicle.battery.critical.limit}")
private static double mCriticalLimit;
#Value("${vehicle.battery.default}")
private static double mTotalPower;
#Value("${vehicle.battery.critical.warning}")
private static String powerWarning;
private VehicleUtil() {
// empty private constructor.
}
public static VehicleUtil getInstance() {
if (mInstance == null) {
synchronized (mLock) {
if (mInstance == null)
mInstance = new VehicleUtil();
}
}
return mInstance;
}
public static void checkBatteryStatus(VehicleData vehicleData) {
double criticalMark = (mCriticalLimit * 100.0f) / mTotalPower;
if (vehicleData.getRemainingBattery() < criticalMark) {
vehicleData.setBatteryCritical(Boolean.TRUE);
System.out.println(powerWarning);
} else {
vehicleData.setBatteryCritical(Boolean.FALSE);
}
}
}
Please let me know how to fix this.
Thank You
You need to inject the properties into a spring-managed bean.
What you can do, is to bundle the properties in a bean like this (see documentation)
#ConfigurationProperties(prefix="vehicle")
public class VehicleProperties {
#Value("${battery.critical.limit}")
private double mCriticalLimit;
#Value("${battery.default}")
private double mTotalPower;
#Value("${battery.critical.warning}")
private String powerWarning;
}
Then, inject the Vehicle Properties into your service and passe them to VehicleUtil:
public class WalkServiceImpl {
#Autowired
private VehicleProperties vehicleProperties;
#Override
public void performTask(VehicleData vehicleData) {
...
VehicleUtil.checkBatteryStatus(vehicleProperties, vehicleData);
}
}
Better yet, I would convert VehicleUtil into a managed-bean
#Component
public class VehicleUtil {
#Autowired
private VehicleProperties vehicleProperties;
}
VehicleData
I'm assuming VehicleData is some business object, passed from the client or that you create yourself, for which they are many instances (not just one singleton). In this case, it wouldn't make sense to convert VehicleData into a managed-bean, and its external dependencies could just be provided to it through its setters:
public class VehicleData {
private double walkingDistance;
private double liftWeight;
private double remainingBattery;
// setters & getters ....
}
Only Instances controlled by Spring are able to get property values injected.
As you have a Spring application you should not write own singleton logic instead of simply annotation VehicleUtil by #Component. This would also make any usages more testable as you can mock it in a test when the util instance is injected.
You cannot inject a value directly to the VehicleData pojo. The property value is mostly a constant during runtime so it can be a default only. You should set the value before exposing in a repo or service or leave it null and use the value instead of the non set property wherever it is used.

Spring : how to get values from properties file in Model class

I am using Spring MVC for project. I have some constant values which is stored in properties file and I want to fetch from properties file. Question I am unable to fetch values in Model Classes from properties file. It is getting null.
I have set property file location in servlet-context.xml
<context:property-placeholder location="classpath:myproperties.properties" />
Now by using #Value annotation I inject value from properties file.
#Component
class ModelTest {
#Value("${fname}")
private String fname;
// Default Constructor
public ModelTest(){
Sysout(fname); // getting null here
}
#PostConstruct
public void initMembers(){
Sysout(fname) // Prints fname value properly
}
public void setFname(String fname){
this.fname=fname;
}
public String getFname(){
return fname;
}
#Override
public String toString() {
Sysout(fname);
return "ModelTest [variableFirst=" + variableFirst + "]";
}
}
Here is ServiceTest class.
#Service
class ServiceTest(){
#Value("${fname}")
private String fname;
public String printTest(){
sysout(fname); // Prints fname value
return new ModelTest().toString() // Prints null
}
}
Here is ControllerHome Class :
#Controller
public class ControllerHome {
#Value("${fname}")
private String fname;
#Autowired
private ServiceTest service;
#RequestMapping("/")
public #ResponseBody String printData(){
sysout(fname); // Prints fname value
return service.printTest(); // Print null
}
}
In model class fname is getting null while In controller and service class value is coming properly.
is anyone face such issue?
When you say model class, do you mean the value passed to a controller method indicated by #ModelAttribute?
If so, that class is created by ordinary constructor invocation through reflection. It is not a spring bean, and thus #Value does nothing.
Addressing your edit, I think there is some fundamental misunderstanding about how Spring works.
#Service
class ServiceTest(){
#Value("${fname}")
private String fname;
public String printTest(){
sysout(fname); // Prints fname value
// Calling new here means Spring does nothing
// ModelTest is not a Spring bean
// `#Component`, `#PostConstruct` and `#Value` in ModelTest mean nothing.
return new ModelTest().toString() // Prints null
}
}
Instead, you have to do something like this:
#Service
class ServiceTest(){
#Value("${fname}")
private String fname;
#Autowired
private ModelTest modelTest;
public String printTest(){
sysout(fname); // Prints fname value
// modelTest is now a Spring bean
return modelTest.toString() // Should not print null
}
}
Now, Spring will create ModelTest, and #Component, #PostConstruct and #Value will be honored by Spring.
However, #Component by itself has a default singleton scope. So, you will have the same modelTest always.
So, you have to do something like this:
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
class ModelTest {
// ...
}
Now, while the modelTest reference in ServiceTest will remain constant, the use of a proxy will divert the method calls to a new instance of ModelTest, created by Spring, per request.
This is how I do it :
#Component
#PropertySource("classpath:myproperties.properties") // <-Add this.
class ModelTest {
#Autowired
private Environment env;
public void test(){
String name = env.getProperty("name"); //Assuming you have a 'name' key in your myproperties.property
}
}

Categories