Spring: How to inject a value to static field?
First of all, public static
non-final
fields are evil. Spring does not allow injecting to such fields for a reason.
Your workaround is valid, you don't even need getter/setter, private
field is enough. On the other hand try this:
@Value("${my.name}")
public void setPrivateName(String privateName) {
Sample.name = privateName;
}
(works with @Autowired
/@Resource
). But to give you some constructive advice: Create a second class with private
field and getter instead of public static
field.
How to make spring inject value into a static field
You have two possibilities:
- non-static setter for static property/field;
- using
org.springframework.beans.factory.config.MethodInvokingFactoryBean
to invoke a static setter.
In the first option you have a bean with a regular setter but instead setting an instance property you set the static property/field.
public void setTheProperty(Object value) {
foo.bar.Class.STATIC_VALUE = value;
}
but in order to do this you need to have an instance of a bean that will expose this setter (its more like an workaround).
In the second case it would be done as follows:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="foo.bar.Class.setTheProperty"/>
<property name="arguments">
<list>
<ref bean="theProperty"/>
</list>
</property>
</bean>
On you case you will add a new setter on the Utils
class:
public static setDataBaseAttr(Properties p)
and in your context you will configure it with the approach exemplified above, more or less like:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="foo.bar.Utils.setDataBaseAttr"/>
<property name="arguments">
<list>
<ref bean="dataBaseAttr"/>
</list>
</property>
</bean>
How to inject property value using @Value into static fields
The only way is to use setter for this value
@Value("${value}")
public void setOutputPath(String outputPath) {
AClass.outputPath = outputPath;
}
However you should avoid doing this. Spring is not designed for static injections. Thus you should use another way to set this field at start of your application, e.g. constructor.
Anyway @Value annotation uses springs PropertyPlaceholder which is still resolved after static fields are initialized. Thus you won't have any advantages for this construction
How to assign a value from application.properties to a static variable?
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).
Configure static field on boot with dependency injection Spring
You can't autowire static field directly, but you can set static field after application is initialized using @PostConstruct
or catching ApplicationReadyEvent
public class MyApp {
@Autowired
TelegramBot telegramBot;
@Autowired
JavaMailSender javaMailSender
@Value("${telegram-bot.default-chat}")
String chatId;
@Value("${mail.default-receiver}")
String to;
@PostConstruct
void setDefaults() {
CustomerNotificationPreference.setDefaults(List.of(
new TelegramNotificationChannel(telegramBot, chatId),
new MailNotificationChannel(javaMailSender, to)
));
}
// OR
@EventListener(ApplicationReadyEvent::class)
void setDefaults() {
// same code as above
}
}
Make spring @Value take default value from static field
You may just want to inject Environment
, and get the value as default like so:
@Configuration
public class MyConfiguration {
public static final String DEFAULT_PROPERTY_VALUE = "long string...";
@Autowired
private Environment env;
@Bean("midPriceDDSEndpoint")
public DistributedDataSpace<Long, MidPriceStrategy> midPriceDDSEndpoint() {
String myPropertyValue = env.getProperty("foo.bar.my-property", DEFAULT_PROPERTY_VALUE);
}
}
I personally think that's a little bit more readable...
Related Topics
Get Button Coordinates and Detect If Finger Is Over Them - Android
How to Chain Async Task Sequentially (Starting One After the Previous Asynctask Completes)
Endless Scrolling Listview Not Working
How to Read Data from Xls (Excel) File [Java, Android]
How to Make Burst Mode Available to Camera
Android Getintent().Getextras() Returns Null
Use Startactivityforresult from Non-Activity
Internet Listener Android Example
How to Combine One Android Studio Project into Another Android Studio Project
How to Override Action Bar Back Button in Android
Eclipse with Android Sdk, Error: Java Heap Space
Values of Counter Changes After Scrolling Expendablelistview
Google Sign in Signed APK Not Working
Android Studio Mailto Intent Doesn't Show Subject and Mail Body