是否可以使用CDI将参数注入方法调用?预期的行为类似于现场注入。查找首选生产者并使用产品。
我想做的是:
public void foo(@Inject Bar bar){
//do stuff
}
或者这个(更少混淆的sytax):
public void foo(){
@Inject
Bar bar;
//do stuff
}
在这两种情况下,此语法都是非法的。还有其他选择吗?如果不是 - 如果有可能,出于某种原因,这会是一个坏主意吗?
谢谢
编辑 - 我可能已经使我的要求不够清楚了 - 我希望能够直接调用该方法,将bar
变量的初始化留给容器。 JörnHorstmann和Perception的回答表明这是不可能的。
答案 0 :(得分:19)
当容器实例化bean时,会为bean处理注入点,这会限制方法级别注入的用例数。规范的当前版本识别以下类型的方法注入:
初始化方法注入
public class MyBean {
private Processor processor;
@Inject
public void setProcessor(final Processor processor) {
this.processor = processor;
}
}
当注入MyBean
的实例时,也会通过它的setter方法注入处理器实例。
事件观察员方法
public class MyEventHandler {
public void processSomeEvent(@Observes final SomeEvent event) {
}
}
事件实例直接注入事件处理方法(但不是@Inject注释)
制作人方法
public class ProcessorFactory {
@Produces public Processor getProcessor(@Inject final Gateway gateway) {
// ...
}
}
生产者方法的参数自动被注入。
答案 1 :(得分:9)
如果你真正想要的不是作为方法的参数(应该由调用者提供),而是每次调用方法并完全构造和注入时正确初始化的CDI bean实例,然后检查
javax.inject.Provider<T>
基本上,首先将提供者注入类
@Inject Provider<YourBean> yourBeanProvider;
然后,在该方法中,获取一个新实例
YourBean bean = yourBeanProvider.get();
希望这会有所帮助:)
答案 2 :(得分:8)
当我最初搜索这个主题时出现了这个问题,我已经了解到随着CDI 1.1的发布(包含在JavaEE 7规范中),现在有一种方法可以实际执行OP所需的操作部分地。你还是做不到
public void foo(@Inject Bar bar){
//do stuff
}
但你可以“注入”一个局部变量,虽然你不使用@Inject
,而是以编程方式查找注入的实例,如下所示:
public void foo() {
Instance<Bar> instance = CDI.current().select(Bar.class);
Bar bar = instance.get();
CDI.current().destroy(instance);
// do stuff with bar here
}
请注意,select()
方法可以选择使用您可能需要提供的任何限定符注释。祝你好运获得java.lang.annotation.Annotation
的实例。迭代Instance<Bar>
可能更容易找到您想要的那个。
我被告知你需要像我上面所做的那样销毁Instance<Bar>
,并且可以从经验中验证上述代码的工作原理;但是,我不能发誓你需要来销毁它。
答案 3 :(得分:5)
CDI的这个功能称为“初始化方法”。语法与您的代码的不同之处在于整个方法都使用@Inject
注释,方法参数可以通过限定符进一步注释以选择特定的bean。 JSR 299的3.9节显示了以下示例,@Selected
是一个限定符,如果只有一个bean实现,则可以省略。
@Inject
void setProduct(@Selected Product product) {
this.product = product;
}
请注意
应用程序可以直接调用初始化方法,但是容器不会将任何参数传递给方法。
答案 4 :(得分:1)
您可以在方法中使用BeanManager API来获取上下文引用,或者根据您可以注入的最终目标
Instance<Bar>
在方法之外并在方法中使用它。
答案 5 :(得分:0)
如果您的目标是通过反射调用该方法,则可以为每个参数创建一个 InjectionPoint
。
以下是使用 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");
}
}
}