Reputation: 61538
Is it possible to use CDI to inject parameters into method calls? The expected behaviour would be similar to field injection. The preferred producer is looked up and the product is used.
What I would like to do is this:
public void foo(@Inject Bar bar){
//do stuff
}
or this (with less confusing sytax):
public void foo(){
@Inject
Bar bar;
//do stuff
}
This syntax is illegal in both cases. Is there an alternative? If no - would this be a bad idea for some reason if it were possible?
Thank you
EDIT - I may have made my requirements not clear enough - I would like to be able to call the method directly, leaving the initialization of the bar
variable to the container. Jörn Horstmann's and Perception's answer suggest that it is not possible.
Upvotes: 26
Views: 40200
Reputation: 1571
If your goal is to call the method via reflection, it is possible to create an InjectionPoint
for each parameter.
Here's an example using CDI-SE:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
public class ParameterInjectionExample {
public static class Foo {
// this method will be called by reflection, all parameters will be resolved from the BeanManager
// calling this method will require 2 different Bar instances (which will be destroyed at the end of the invocation)
public void doSomething(Bar bar, Baz baz, Bar bar2) {
System.out.println("got " + bar);
System.out.println("got " + baz);
System.out.println("got " + bar2);
}
}
@Dependent
public static class Bar {
@PostConstruct
public void postConstruct() {
System.out.println("created " + this);
}
@PreDestroy
public void preDestroy() {
System.out.println("destroyed " + this);
}
}
@ApplicationScoped
public static class Baz {
@PostConstruct
public void postConstruct() {
System.out.println("created " + this);
}
@PreDestroy
public void preDestroy() {
System.out.println("destroyed " + this);
}
}
public static Object call(Object target, String methodName, BeanManager beanManager) throws Exception {
AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(target.getClass());
AnnotatedMethod<?> annotatedMethod = annotatedType.getMethods().stream()
.filter(m -> m.getJavaMember().getName().equals(methodName))
.findFirst() // we assume their is only one method with that name (no overloading)
.orElseThrow(NoSuchMethodException::new);
// this creationalContext will be valid for the duration of the method call (to prevent memory leaks for @Dependent beans)
CreationalContext<?> creationalContext = beanManager.createCreationalContext(null);
try {
Object[] args = annotatedMethod.getParameters().stream()
.map(beanManager::createInjectionPoint)
.map(ip -> beanManager.getInjectableReference(ip, creationalContext))
.toArray();
return annotatedMethod.getJavaMember().invoke(target, args);
} finally {
creationalContext.release();
}
}
public static void main(String[] args) throws Exception {
try (SeContainer container = SeContainerInitializer.newInstance().disableDiscovery().addBeanClasses(Bar.class, Baz.class).initialize()) {
System.out.println("beanManager initialized");
call(new Foo(), "doSomething", container.getBeanManager());
System.out.println("closing beanManager");
}
}
}
Upvotes: 0
Reputation: 1775
If what you REALLY want is not something as the parameter of the method (which should be provided by the caller), but a properly initialized instance of a CDI bean each time when the method is called, and fully constructed and injected, then check
javax.inject.Provider<T>
Basically, first inject a provider to the class
@Inject Provider<YourBean> yourBeanProvider;
then, in the method, obtain a new instance
YourBean bean = yourBeanProvider.get();
Hope this helps :)
Upvotes: 12
Reputation: 7396
This question came up when I originally did a search on this topic, and I have since learned that with the release of CDI 1.1 (included in the JavaEE 7 spec), there is now a way to actually do what the OP wanted, partially. You still cannot do
public void foo(@Inject Bar bar){
//do stuff
}
but you can "inject" a local variable, although you do not use @Inject
but rather programmatically look up the injected instance like this:
public void foo() {
Instance<Bar> instance = CDI.current().select(Bar.class);
Bar bar = instance.get();
CDI.current().destroy(instance);
// do stuff with bar here
}
Note that the select()
method optionally takes any qualifier annotations that you may need to provide. Good luck obtaining instances of java.lang.annotation.Annotation
though. It may be easier to iterate through your Instance<Bar>
to find the one you want.
I've been told you need to destroy the Instance<Bar>
as I have done above, and can verify from experience that the above code works; however, I cannot swear that you need to destroy it.
Upvotes: 8
Reputation: 17872
You can use the BeanManager API in your method to get contextual references, or depending on your ultimate goal you could inject an
Instance<Bar>
outside of the method and use it in the method.
Upvotes: 1
Reputation: 80593
Injection points are processed for a bean when it is instantiated by the container, which does limit the number of uses cases for method level injection. The current version of the specification recognizes the following types of method injection:
Initializer method injection
public class MyBean {
private Processor processor;
@Inject
public void setProcessor(final Processor processor) {
this.processor = processor;
}
}
When an instance of MyBean
is injected, the processor instance will also be injected, via it's setter method.
Event Observer Methods
public class MyEventHandler {
public void processSomeEvent(@Observes final SomeEvent event) {
}
}
The event instance is injected into the event handling method directly (though, not with the @Inject annotation)
Producer Methods
public class ProcessorFactory {
@Produces public Processor getProcessor(@Inject final Gateway gateway) {
// ...
}
}
Parameters to producer methods automatically get injected.
Upvotes: 20
Reputation: 34014
That feature of CDI is called an "initializer method". The syntax differs from your code in that the whole method is annotated with @Inject
, the method parameters can further be annotated by qualifiers to select a specific bean. Section 3.9 of JSR 299 shows the following example, with @Selected
being a qualifier that can be omitted if there is only one bean implementation.
@Inject
void setProduct(@Selected Product product) {
this.product = product;
}
Please note that
The application may call initializer methods directly, but then no parameters will be passed to the method by the container.
Upvotes: 5