Is there a way to set up such enum values via Spring IoC at construction time?
What I would like to do is to inject, at class load time, values that are hard-coded in the code snippet below:
public enum Car
{
NANO ("Very Cheap", "India"),
MERCEDES ("Expensive", "Germany"),
FERRARI ("Very Expensive", "Italy");
public final String cost;
public final String madeIn;
Car(String cost, String madeIn)
{
this.cost= cost;
this.madeIn= madeIn;
}
}
Let's say that the application must be deployed in Germany, where Nanos are "Nearly free", or in India where Ferraris are "Unaffordable". In both countries, there are only three cars (deterministic set), no more no less, hence an enum, but their "inner" values may differ. So, this is a case of contextual initialization of immutables.
Here is the solution I came to (thanks to Javashlook whose answer put me on track). It works, but it's most probably not a production-grade way of doing it.
But better than a thousand words, here is the code, I'll let you judge by yourself.
Let's take a look at the revised
Car
enum :And here are the "plumbling" classes :
Finally, the applicationContext definition :
It works, but there is one major weakness :
CarEnumerationInitializer
MUST be instantiated BEFORE any reference is made toCar
enumeration, otherwise CarProperties are null, meaning that Car's properties can't be set whenCar
is loaded (hence theIllegalStateException
thrown, to at least make it crashes in a predictable and documentated way).carInitializer
bean's propertylazy-init
set to an explicitfalse
, to put emphasis on the need to load it as soon as possible. I would say it may be useful in a simple application, one where you can easely guess where a first call toCar
will be made. For a larger one, it will probably be such a clutter that I didn't encourage you to use it.Hope this help, comments and vote (up and down) very welcome :) I'll wait for a few days to make this one the accepted answer, to let you react.
You can't create new enum values via Spring, they must be declared in the class. However, since the enum values will be singletons anyway (created by the JVM), any configurations that should be set, or services to be injected, can be done via invoking static methods in the enum class:
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/factory/config/MethodInvokingFactoryBean.html
OK, it's quite fiddly, but it CAN be done.
It's true that Spring cannot instantiate enums. But that's not a problem - Spring can also use factory methods.
This is the key component:
Then the following test class shows that it works:
Attempting to mutate an Enum is well silly and goes completely against their design objectives. An enum by definition represents a distinct value within a group. If you ever need more / less values you will need to update the source. While you can change an enums state by adding setters (after all they are just objects) your hacking the system.
I have faced the same issue when I was working to localize my enum label in different locales.
Enum Code:
My I18NTranslator class which basically load the message source to get localized content.
I18Ntransalator
class depends onspringContext
if you don't write you might face a peculiar bug. Some time might face a dependency related which causes null pointer exception. I had put a lot of effort to resolve this issue.We have to set the spring context
Now it is time to define the bean for I18NMessageSource.
PS: if you need the custom interceptor code I can share in the comment. Defines all local properties files inside resources/i18n folder with messages prefix like
messages_en.properties
for english andmessages_fr.properties
fro french.I don't think it can be done from Spring's
ApplicationContext
configuration. But, do you really need it done by Spring, or can you settle for simple externalization using ResourceBundle; like this:In the properties file, one for each specific locale, enter the keys describing the possible internal enum values:
The only drawback of this approach is having to repeat the name of enum fields (cost, madeIn) in Java code as strings. Edit: And on the plus side, you can stack all properties of all enums into one properties file per language/locale.