How to @Inject Enum values into CDI beans
I was recently asked how to Inject a Java Enum into a bean constructor using CDI. This turns out to be a fairly interesting situation, because enums are not usually available for Injection.
The first attempt
As much as we would hope for this to work, it does not. Enums are not beans because they have no default constructor (hence CDI does not know how to construct them,) and there is no standard way to resolve which enumerated value should be injected by default (Unless there is only one value, but this is still not supported due to the lack of default constructor.)
public class InjectedObject { private MyEnum e; @Inject public InjectedObject(MyEnum e) { this.e = e; } public MyEnum getValue() { return e; } /** * Our enum */ public enum MyEnum { ONE } }
This will result in a deployment failure
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [MyEnum] with qualifiers [@Default] at injection point [[BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] @Inject public org.example.InjectedObject(MyEnum)] at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:365) at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:297) at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:157) at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:184) at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:470) at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:74) at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:72) at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:60) at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:53) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) ... 1 more |
The solution
We must turn to producer methods in order to make this possible. It takes just a little bit of code, but it is possible, and very easy! The reason this works is because any value may be injected if it resolves to a unique set of qualifiers and types. You could even inject a java.lang.String
using this technique.
/** * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public class InjectedObject { private MyEnum e; @Inject public InjectedObject(MyEnum e) { this.e = e; } public MyEnum getValue() { return e; } /** * A producer is required in order to {@link Inject} an Enum */ @Produces public static MyEnum getEnum() { return MyEnum.THREE; } /** * Our enum */ public enum MyEnum { ONE, TWO, THREE } }
And there you have it! A perfectly functional method of injecting enum values in CDI. You will, however, need to make sure you know which type of enum you want to inject, and if you need multiple types, then you’ll need to create qualifiers for each value.
You could also use the built-in @Named qualifier
The @Named
qualifier is a built in qualifying type that allows unique qualifiers to be created using String values.
/** * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public class InjectedObject { private MyEnum e1; private MyEnum e2; @Inject public InjectedObject(@Named("ONE") MyEnum e1, @Named("TWO") MyEnum e2) { this.e1 = e1; this.e2 = e2; } public MyEnum getValue1() { return e1; } public MyEnum getValue2() { return e2; } /** * A producer is required in order to {@link Inject} an Enum */ @Produces @Named("ONE") public static MyEnum getEnumOne() { return MyEnum.ONE; } @Produces @Named("TWO") public static MyEnum getEnumTwo() { return MyEnum.TWO; } /** * Our enum */ public enum MyEnum { ONE, TWO, THREE } }
We would not, however, be able to @Inject @Named("THREE") MyEnum e
because we have not declared a producer with such a qualifier!
Cool article about CDI producer methods!
I modified it a bit to support a "dynamic producer" based on a Config qualifier. This makes it possible to add a value to the enum and directly inject it without adding a new producer:
As some might have guessed, the e3 variable is injected as null because no such enum value exists(but ‘@Config("FOO") MyEnum e3’ would have been possible)
Hi Lincon and wOmbat, please help me.
I try use PasswordConfirmValidator from soicalpm in my project.
@Inject
private InputElement<String> password;
Compilation was sucessfully, but when I try run at Jboss AS7, I get:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [InputElement<String>] with qualifiers [@Default] at injection point [[field] @Inject private org.eqaula.glue.security.validator.SignupValidator.password
I read this article, I have a question: Where I can create a Producer for InputElement<String>?
Hey José Lus Granda,
you would have to create a producer in any class you want. I should look somehow similar to the code below:
Hope this helped!
Cheers,
w0mbat
Using the @Named Qualifier, is there a way to define a default implementation that is used when no explicit name is given at the injection point?
@Named is actually not a qualifier, it just specifies the EL bean name.
I see, never used that before. Thanks for the hint.