From reading the Weld docs I don't think this can be done, but I could really do with it, so thought I'd ask - if there's not a way with CDI, maybe there's a pattern workaround...
I've created a custom qualifier with a member:
@Qualifier
@Target({TYPE, METHOD, FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface JobBinding {
JobType value();
}
JobType is an enum:
public enum JobType {
JOB_A,
JOB_B,
// etc - there are quite a few
Say most jobs need building in a slightly different way, so I have builder classes related to the jobs. These are annotated with a JobBinding annotation given the relevant JobType:
@JobBinding(JobType.JOB_A)
public class JobABuilder implements JobBuilder {
....
When I need to build, I use programmatic lookup:
@Inject @Any
private Instance<JobBuilder> builderSource;
private JobType myJobType;
...
builderSource.select(new JobBindingQualifier(myJobType).get();
JobBindingQualifier is a custom class:
public class JobBindingQualifier extends AnnotationLiteral<JobBinding> implements JobBinding {
private static final long serialVersionUID = -822150300665931157L;
private JobType ty开发者_运维问答pe;
public JobBindingQualifier(JobType type) {
this.type = type;
}
@Override
public JobType value() {
return type;
}
}
So far, great - CDI working brilliantly. However, what if 2 of these jobs, JOB_X and JOB_Y, are built in exactly the same way? I only need one builder class, which I'd like to be instantiated for either of those options - new JobBindingQualifier(JobType.JOB_X)
or new JobBindingQualifier(JobType.JOB_Y)
.
If I annotate JobXAndYBuilder
with both @JobBinding(JOB_X)
and @JobBinding(JOB_Y)
, I get a compiler error about the duplicated annotation. To get around this I could change the value of the annotation to an array of JobTypes, and you would annotate the builder like
@JobBinding(JobType.JOB_X, JobType.JOB_Y)
with the constructor called there using an ellipsis to produce the array. However, if I did that, how could I look that up programmatically using either of the jobTypes? Weld docs suggest that you would have to have both; I'd need to provide the exact arguments:
builderSource.select(new JobBindingQualifier(JobType.JOB_X, JobType.JOB_Y).get();
when I want either to be sufficient to lookup the class:
builderSource.select(new JobBindingQualifier(JobType.JOB_X).get();
//or
builderSource.select(new JobBindingQualifier(JobType.JOB_Y).get();
Using the array really just changes the value that you have to match when looking up. I really need a way of annotating a class twice with the same qualifier annotation, and then being able to look it up with any combination of them. Otherwise I'll have to provide a builder class each for X and Y, when one would suffice. Any ideas? Thanks in advance!
As I said in my comment there is no straight way to have an OR relation when you have two or more qualifiers on a bean class.
So the solution to have the same Bean with another qualifier is to use the Producer mechanism. In your example you can create you first class as usual :
@JobBinding(JobType.JOB_X)
public class JobABuilder implements JobBuilder {
....
}
and after that create a producer method in either in the first class or in a dedicated producers class like that
public class MoreJobsProducer {
@Produces
@JobBinding(JobType.JOB_Y)
protected JobBuilder prodJobYBuilder(@New @JobBinding(JobType.JOB_X)
JobBuilder theJob) {
return theJob;
}
}
In the paramaters of the producer method you Inject your former bean with its own qualifier and the @New
qualifier which create a new instance of the bean to be sure you avoid some scoping issues (read the Weld doc for more info).
It should do the "Job".
精彩评论