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

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
}
}

Related

How to create a bean and autowire at run time dynamically based on user input param

How to create Student class object at run time dynamically based on the parameters received in the URL and inject in to WebapplicationContext so the IoC container can auto wire it automaticallly to Access class?
I need to create a bean at run time based on user parameters.
#RestController
public class FunRestController {
#GetMapping("/{id}/{name}")
public String welcomeToBoot(#PathVariable int id, #PathVariable String name) {
// How to create Student class object at run time dynamically based
// on the param received in the URL and can auto wire it dynamically to ***Access*** class below ?
return "Welcome " + name;
}
}
I need to Autowire a run time bean created
public class Access {
#Autowired
Student s;
void print() {
System.out.println(s.name);
}
}
POJO:
public class Student {
public int id;
public String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
I would suggest not to #Autowired the Student object but instead, pass it as a parameter to a function. Something as follows:
public class Access {
void print(Student s) {
System.out.println(s.name);
}
}
Now you just need to call print() method with the given Student. If you need Access to be a Spring-managed Bean so that you can inject it into your Controller, you would need to annotate it with #Component.
Instead of creating bean, you can create a thread local variable and initialise it as the first thing. Then it'll be available throughout the request / response scope

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.
}

Convert legacy code to Spring dependency injection

The legacy code snippet is shown as below. What I want to do is to convert this code to Spring. But the problem is Spring managed the dependency on it's own. My question is how to inject the serviceId provided by constructor?
public class MyService{
public Attribute getAttribute(){
int serviceId =1;
new ServiceDao(serviceId).getAttribute();
}
}
class ServiceDao{
private int serviceId;
ServiceDao(int serviceId){
this.serviceId = serviceId;
}
public Attribute getAttribute(){
//to get attribute
}
}
Basically you are trying to create new objects each time you call
new ServiceDao(serviceId).getAttribute();
This is purely against dependency injection. As your logic is based on the service ID you can create a service class as follows
#Service
class ServiceDao{
ServiceDao(){
}
public Attribute getAttribute(int serviceId){
//to get attribute
//return attribute based on service Id,
//if(serviceId==1)
//{ return new Attribute("Red");}
}
}
Your Myservice can be something like this
#Service
public class MyService{
#Autowired
ServiceDao dao;
public Attribute getAttribute(){
int serviceId =1;
return dao.getAttribute(1);
}
}

Property values in bean class are null in constructor

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);
}

Categories