Tuesday, May 29, 2007

Creating a Spring Bean from a Java 5 Enum

Instances of Java enums, introduced in Java 5, can be a bit tricky to instantiate as Spring beans because the Java compiler hides enum constructors. However, we can create a bean from an enum by using the enum valueOf static method as a factory method.

For example, let's say we have defined an enum class as follows:


package robertmaldon.cafe;

public enum Coffee {
MOCHA,
ESPRESSO,
LATTE,
FRAPPUCCINO;
}


The Spring XML config for a bean instance then looks something like this:


<bean id="latte" class="robertmaldon.cafe.Coffee" factory-method="valueOf">
<constructor-arg>
<value>LATTE</value>
</constructor-arg>
</bean>


NOTE: Declaring the bean scope as prototype or singleton makes no difference. For the same class loader a singleton bean is always returned.

If you don't want to declare a bean for each value of an enum you can simply use the enum value name at the point where it is injected into a dependent class. For example, let's say you have a class that is injected with an instance of our enum above:


package robertmaldon.cafe;

public class CoffeeMachine {
private Coffee coffee;

public void setCoffee(Coffee coffee) {
this.coffee = coffee;
}
}


In the Spring XML config simply use the enum value as the property value (apparently works with Spring 1.2.2 and beyond):


<bean id="machine" class="robertmaldon.cafe.CoffeeMachine">
<property name="coffee" value="MOCHA"/>
</bean>

12 comments:

Vinuth said...

I have the following enum:

public enum TabList {
PEOPLE(Category.PEOPLE, "jsp/peopleRecords.jsp");
COMMUNITY(Category.COMMUNITY, "jsp/communityRecords.jsp");
}


Now how can I make a bean of this enum and inject the view names in the argument (eg: "jsp/peopleRecords.jsp") from spring XML?

Robert Maldon said...

Sorry for not responding much, much sooner.

In the example you gave you would simply use the enum value "PEOPLE" or "COMMUNITY". Since it is an enum the view names are fixed at compile time.

Gubbi said...

Thanks a lot !!

- Vinuth.

Владимир Долженко said...

Sorry, why standard spring methods doesn't work ???

http://static.springframework.org/spring/docs/2.5.x/reference/xsd-config.html#xsd-config-body-schemas-util-constant

<bean id="..." class="...">
<property name="isolation">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>

or even

<bean id="..." class="...">
<property name="isolation">
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</property>
</bean>

Владимир Долженко said...

Oh, I'm sorry. Of cource, described method works fine, but only destination type is known - if destination type is someone interface, not concrete enum, spring couldn't get enum by it's name.
And in this cases method described by me below helps.

Anonymous said...

I often find myself using enum style singletons in places where i do not have access to the spring context.

Is this bad practice?

Contrived example:

public enum Settings{
INSTANCE;
private Boolean debug;
public void setDebug(Boolean b){this.debug= debug}
public Boolean isDebug(){ return debug; }
}

<bean id="neverUsed" class="Settings" factory-method="valueOf">
<property name="debug" value="${my.properties.debug}"/>
</bean>

Now, assume i'm in a class that was not instantiated by spring and is not context aware. But i'm running in a JVM that has some objects that spring created.
if( Settings.INSTANCE.getDebug() ){
...
}

Robert Maldon said...

Anonymous,

That's an interesting question.

Many consider static singletons a bad practice because you can't swap the underlying implementation with another one without making code changes.

In the situation you presented you will need something to link between non-Spring-created and Spring-created objects.

Static Singleton is a common way of doing this.

Another alternative is to use a Factory pattern, and the Factory behind the scenes either gets things from Spring or creates them programatically.

A Factory is more code than the Static Singleton in your example, but might be a better way to hide object creation details from the rest of your code. And get rid of static singletons. (Although you typically access a Factory using static methods :))

Anonymous said...

First, thanks for the example! My enum class is set up exactly like yours, just different class names. When I try this, I get a Class Not Found Exception. Using your example, the error would be "ClassNotFoundException: robertmaldon.cafe.Coffee". The only difference that I see between my code and yours is that the bean I am creating from an enum is an inner bean.

Any ideas?

Thanks in advance!

Janek said...

Thanks! This was exactly what I needed to know.

Ramy said...

Thanks for the article, it helped me :)

Ramy Nagah

Anonymous said...

This was just what I needed! This was missed in all the Spring books I read.

Peter said...

thanks, exactly what I needed