How to assign a value from application.properties to a static variable? - java

I am using Spring MVC. I have a UserService class annotated with #Service that has a lot of static variables. I would like to instantiate them with values from the application.properties file.
For example in application.properties I have: SVN_URL = http://some.url/repositories
Then in the class there is: #Value("${SVN_URL}") private static String SVN_URL
I get the Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
I have also tried #Autowired private static Environment env;
And then: private static String SVN_URL=env.getProperty("SVN_URL");
It gives the same error.

Think about your problem for a second. You don't have to keep any properties from application.properties in static fields. The "workaround" suggested by Patrick is very dirty:
you have no idea when this static field is modified
you don't know which thread modifies it's value
any thread at any time can change value of this static field and you are screwed
initializing private static field that way has no sense to me
Keep in mind that when you have bean controlled by #Service annotation you delegate its creation to Spring container. Spring controls this bean lifecycle by creating only one bean that is shared across the whole application (of course you can change this behavior, but I refer to a default one here). In this case any static field has no sense - Spring makes sure that there is only one instance of UserService. And you get the error you have described, because static fields initialization happens many processor-cycles before Spring containers starts up. Here you can find more about when static fields are initialized.
Suggestion
It would be much better to do something like this:
#Service
public class UserService {
private final String svnUrl;
#Autowired
public UserService(#Value("${SVN_URL}") String svnUrl) {
this.svnUrl = svnUrl;
}
}
This approach is better for a few reasons:
constructor injection describes directly what values are needed to initialize the object
final field means that this value wont be changed after it gets initialized in a constructor call (you are thread safe)
Using #ConfigurationProperties
There is also another way to load multiple properties to a single class. It requires using prefix for all values you want to load to your configuration class. Consider following example:
#ConfigurationProperties(prefix = "test")
public class TestProperties {
private String svnUrl;
private int somePort;
// ... getters and setters
}
Spring will handle TestProperties class initialization (it will create a testProperties bean) and you can inject this object to any other bean initialized by Spring container. And here is what exemplary application.properties file look like:
test.svnUrl=https://svn.localhost.com/repo/
test.somePort=8080
Baeldung created a great post on this subject on his blog, I recommend reading it for more information.
Alternative solution
If you need somehow to use values in static context it's better to define some public class with public static final fields inside - those values will be instantiated when classloader loads this class and they wont be modified during application lifetime. The only problem is that you won't be able to load these values from Spring's application.properties file, you will have to maintain them directly in the code (or you could implement some class that loads values for these constants from properties file, but this sounds so verbose to the problem you are trying to solve).

Spring does not allow to inject value into static variables.
A workaround is to create a non static setter to assign your value into the static variable:
#Service
public class UserService {
private static String SVN_URL;
#Value("${SVN_URL}")
public void setSvnUrl(String svnUrl) {
SVN_URL = svnUrl;
}
}

Accessing application.properties in static member functions is not allowed but here is a work around,
application.properties
server.ip = 127.0.0.1
PropertiesExtractor.java
public class PropertiesExtractor {
private static Properties properties;
static {
properties = new Properties();
URL url = new PropertiesExtractor().getClass().getClassLoader().getResource("application.properties");
try{
properties.load(new FileInputStream(url.getPath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static String getProperty(String key){
return properties.getProperty(key);
}
}
Main.class
public class Main {
private static PropertiesExtractor propertiesExtractor;
static{
try {
propertiesExtractor = new PropertiesExtractor();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static getServerIP(){
System.out.println(propertiesExtractor.getProperty("server.ip")
}
}

static String profile;
#Value("${spring.profiles.active:Unknown}")
private void activeProfile(String newprofile) {
profile = newprofile;
};

In order to gain static access to Spring Boot properties you can create a Properties Holder Component which implements the Command Line Runner interface. The command line runner interface executes run() upon component instantiation by Spring Boot.
Since we have autowired access to our properties object in the PropertiesHolder component, it is possible to assign the autowired properties to a static Properties class variable upon CommandLineRunner execution of the run() method.
At this point any class can statically call PropertiesHolder.getProperties() to access the contents of Spring Boot properties values.
PropertiesHolder.class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class PropertiesHolder implements CommandLineRunner {
//Spring Boot Autowired Properties Object
#Autowired MyProperties myAutowiredProperties;
//Statically assigned Properties Object
private static MyProperties properties;
//Hide constructor (optional)
private PropertiesHolder(){}
public static MyProperties getProperties() throws NullPointerException{
if(PropertiesHolder.properties == null)
throw new NullPointerException("Properites have not been initialized by Spring Application before call.");
return PropertiesHolder.properties;
}
//Use to assign autowired properties to statically allocated properties
public static void makeAvailable(MyProperties myAutowiredProperties){
PropertiesHolder.properties = myAutowiredProperties;
}
//Spring Boot command line runner autoexecuted upon component creation
//which initializes the static properties access
public void run(String... args) {
PropertiesHolder.makeAvailable(myAutowiredProperties);
}
}
MyProperties.class
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
//Example: your_properties_file_prefix.properties
#ConfigurationProperties(prefix = "YOUR_PROPERTIES_FILE_PREFIX")
#Component
#Data
public class MyProperties {
private String property1;
private String property2;
private String property3;
}

At least one more simple solution with configuration file:
#Configuration
public class YourStaticPropertyConfiuration {
public static String PROPERTY_NAME;
#Value("${propertyName}")
public void setProperty(final String propertyName) {
PROPERTY_NAME = propertyName;
}
}
Use PROPERTY_NAME anywhere as static variable

For all those who, for whatever reason, want to provide setting properties imported from files as static properties, here is a solution that is as simple and safe as possible.
The Problem:
Spring Boot unfortunately doesn't provide a simple way to import properties from a file and bind them as static properties to a class. One possible solution to achieve that anyway would be to set the static properties using `#Value` annotations like this:
public class GlobalProperties {
public static String NAME;
#Value("${configs.boss.name}")
public void setName(String name) {
NAME = name;
}
}
However, this approach would mean that the static properties cannot be declared as final.And we certainly don't want that.
The Solution:
application.yml:
configs:
boss:
name: "Leeloo Multipass"
ConfigProperties.java:
#Validated
#ConfigurationProperties(prefix = "configs")
public record ConfigProperties(#NotNull Boss boss) {
public static final String BOSS_NAME = BOSS.NAME;
private static class Boss {
private static String NAME;
public Boss(String name) {
NAME = name;
}
}
}
The solution is based on the assumption that Spring Boot builds the configuration objects first, and the properties' configuration objects first of all. So at the time Spring Boot adds the prepared objects as Bean to the context, the nested classes are already setup and the static properties initialization of ConfigProperties can access the static properties of the nested classes (which still are not final, but also not accessible from outside). This way it is possible to provide all properties declared as static final. Unless Spring Boot doesn't decide to change its internal initialization process, everything is cream & cookie.
This approach was tested with Spring Boot 3 and Java 17. It is of course possible to provide the properties additionally via the configs-bean. For that, the properties of the nested classes must be explicitly specified and their corresponding getters must be implemented. In this case, some simplification can be achieved by using records instead of classes.

Related

How to inject a static dependency in an utility class with no constructor

I have the following utility class with some static methods and static fields.
Now I need to add a dependency into this class. This is how I am trying to do :
public class MyUtil {
private static final List<String> status = Arrays.asList(new String[] { MyStatus.SUCCESS, MyStatus.FAILURE });
#Inject
public static MyDBCache myDBCache; // newly added field
#Inject
public static Configuration myConfig; // newly added field
public static Boolean somePublicMethod() {
// some code here
// myConfig --> will read value from conf file.
// myDBCache will call my cache configured to read some value.
}
private static String somePrivateMethod() {
// some code here
}
}
My question is : Is this a valid way to perform dependency injection and will this ensure this will actually create the dependency object at runtime. I know I can use #Inject to a field which is non-static and , and I can inject the dependency via the constructor. But as this is a simple utility class with no explicit constructor, I ama little confused how to progress. And also this is not a spring boot project.
Please help !

How to use values from application.properties in named properties of annotations on class header

It is known that some annotations have named properties, such as:
#MyAnnotation(name="myName", url="http://www.example.com")
Now, if I were to use this annotation in MyClass like this:
#MyAnnotation(name="myName", url="http://www.example.com")
public class MyClass {
...
}
but move the values "myName" and "http://www.example.com" into application.properties like this:
services.name=myName
services.url=http://www.example.com
then how can I access these properties from MyClass?
I was able to use the properties defined in application.properties as below:
#PropertySource("classpath:application.properties")
#MyAnnotation(name="${services.name}", url="${services.url}")
public class MyClass {
...
}
However, I am not sure if others can use application.properties values like this as well.
You can achieve binding using #ConfigurationProperties annotation.
#Configuration
#ConfigurationProperties(prefix = "services")
public class MyClass {
private String name;
private String url;
}
We use #Configuration so that Spring creates a Spring bean in the application context.
#ConfigurationProperties works best with hierarchical properties that all have the same prefix; therefore, we add a prefix of mail.
The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
Note: If we don't use #Configuration in the POJO, then we need to add #EnableConfigurationProperties(ConfigProperties.class) in the main Spring application class to bind the properties into the POJO:
#SpringBootApplication
#EnableConfigurationProperties(MyClass.class)
public class EnableConfigurationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}
Spring will automatically bind any property defined in our property file that has the prefix services and the same name as one of the fields in the ConfigProperties class.
References :
https://www.baeldung.com/configuration-properties-in-spring-boot
You can simply use #Value annotation. For that your class must managed by Spring. In other words your class must be a Spring Bean.
Something like this,
#Service
public class MyClass {
#Value("${services.name}")
private String name;
#Value("${services.url}")
private String url;
}
Note: For more info here
You can also create custom configuration property class. Something like this,
#Configuration
#ConfigurationProperties("services")
public class MyClass {
private String name;
private String url;
}
Note: For more info here

Injecting a single Spring bean instance into a static variable of a utility class

I have this web application built with Spring and Vaadin, in which I wanted to do this, for the sake of convenience:
Create a utility class that wraps a Spring service, and allows the use of its static methods throughout the application, without having to worry about injecting this service everywhere, like so:
String configurationValue = ConfigurationUtil.getString("some.property.key");
If you work with Vaadin, you might see how convenient this is, because the whole presentation layer is written in Java and you can't always inject Spring services into your Vaadin components as these Vaadin components are not always Spring components themselves.
So this is my utility class:
public final class ConfigurationUtil {
// this is the spring service:
private static ConfigurationService configurationService;
public static void setConfigurationService(final ConfigurationService configurationService) {
ConfigurationUtil.configurationService = configurationService;
}
public static String getString(final String key) {
return configurationService.getString(key);
}
}
This is my service:
#Service("configurationService")
public class ConfigurationServiceImpl implements ConfigurationService, BeanFactoryAware {
private final FrameworkService frameworkService;
#Autowired
public ConfigurationServiceImpl(final FrameworkService frameworkService) throws IOException, ConfigurationException {
// this is where I set this service bean to the utility class
ConfigurationUtil.setConfigurationService(this);
this.frameworkService = frameworkService;
}
public String getString(String key) {
// code that retrieves a configuration value from our configuration files
}
}
My question here is: I'm a bit worried about this causing a bottleneck to access the configuration service, as multiple threads will be calling it, from multiple user sessions. Would this be a problem? Please explain why. Also, feel free to point out other problems with this solution.
I suggest to create a bean that implements ApplicationContextAware like this:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext ac) {
context = ac;
}
public static String getString(final String key) {
ConfigurationService configurationService = context.getBean(ConfigurationService.class);
return configurationService.getString(key);
}
public static <T> T bean(Class<T> beanType) {
return context.getBean(beanType);
}
}
You can create a method like in the example to give static access to Spring Beans or what you requested to get a String from your ConfigurationService.
Btw. I use this a lot in Vaadin applications because I don't want to make every component a Spring Bean.

Spring override properties at runtime and keep them persistent

I am using Spring 3.2.8 and keep my settings in a properties file. Now I want some of them override at runtime. ​​I want to keep the new values persistent by overwriting the old values in the properties file​​.
How can I do this in Spring? Some properties ​​I inject with # Value and others get with MessageSource.getMessage(String, Object [], Locale). The beans are already instantiated with these values​​. How can I access the properties, store them and update all beans system wide?
Thanks!
OK, given your follow up answers I would keep this fairly simple and use what you already know of Spring. I'll make some assumptions that annotation configuration is OK for you.
In my example, I'll assume all the properties that you want to configure relate to something called a ServerConfiguration and that initially these are read from server.properties on the classpath.
So part 1, I would define a bean called ServerProperties that has the original values from the server.properties injected into it.
So:
#Component
public class ServerProperties
{
#Value("${server.ip}");
private String ipAddress;
...
public void setIpAddress(String ipAddress)
{
this.ipAddress = ipAddress;
}
public String getIpAddress()
{
return this.ipAddress;
}
}
Secondly, anywhere that relies on these properties, I would inject an instance of ServerProperties rather than using #Value e.g:
#Component
public class ConfigureMe
{
#AutoWired
private ServerProperties serverProperties;
#PostConstruct
public void init()
{
if(serverProperties.getIpAddress().equals("localhost")
{
...
}
else
{
...
}
}
}
Thirdly I would expose a simple Controller into which ServerProperties is injected so that you can use your web page to update system properties e.g:
#Controller
public class UpdateProperties
{
#AutoWired
private ServerProperties serverProperties;
#RequestMapping("/updateProperties")
public String updateProperties()
{
serverProperties.setIpAddress(...);
return "done";
}
Finally, I would use #PreDestroy on ServerProperties to flush the current property values to file when the ApplicationContext is closed e.g:
#Component
public class ServerProperties
{
#PreDestroy
public void close()
{
...Open file and write properties to server.properties.
}
}
That should give you a framework for what you need. I'm sure it can be tweaked, but it will get you there.

Loading static properties file class

Currently I am using a singleton class to read and load the propertied file. I get an instance of this in any class where I want to use a property value.
Would not it be better to use a static class instead which can be loaded once (when server started or something .. ) instead of using a singleton ? Why and why not ?
Moreover how can we load a static class OnServerStart or when war gets deployed.
PS: Project is web application
Singleton is better for dependency injection and unit testing than statics.
You may inject an instance of singleton class or a Mock of that type to any other class under test.
public class PropertiesHolder {
private static final PropertiesHolder INSTANCE = new PropertiesHolder();
private final Properties props;
private PropertiesHolder() {
props = load();
}
public static PropertiesHolder getInstance() {
return INSTANCE;
}
public String getProperty(String key) {
return props.getProperty(key);
}
private Properties load() {
...
}
}
Then you may mock PropertiesHolder in your test:
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#Mock private PropertiesHolder holder;
#Test
public void testSomething() {
SomeService service = new SomeService(holder);
when(holder.getProperty("foo")).thenReturn("bar");
String result = service.doSomething();
assertEquals(...)
}
}
For production code you may use:
new SomeService(PropertiesHolder.getInstance());
Or even better, use DI framework, e.g. Spring, for wiring a beans. PropertiesHolder would be a generic bean with factory method getInstance() and the scope 'singleton'.
If you're using Spring in your web application I'd suggest using with it's PropertyPlaceholderConfigurer.
If you don't want to use Spring for that and need to do some actions (e.g. loading property file) on servlet when webapp is started then use ServletContextListener as Bhesh Gurung has suggested.

Categories